Skip to main content

Usage guidelines

|

Some features, such as police and speed camera reports, may be illegal in certain countries. Ensure that the safety overlay and the corresponding entries in socialReports overlay are disabled where applicable. Additionally, restrict the availability of the SocialOverlay features based on current local regulations.

The Magic Lane Android SDK utilizes data from OpenStreetMap. Please ensure proper attribution is given in accordance with the OpenStreetMap license.

Any use or display of Wikipedia content must include appropriate attribution, as outlined in the Reusers' rights and obligations section of Wikipedia's copyright guidelines.

SDK thread

The Maps SDK for Android is designed to be thread-safe. Most operations must be performed on specific threads to ensure optimal performance and avoid potential issues. You can interact with SDK objects only from the SDK thread. The SDK thread is initialized when the SDK is initialized and runs in the background. You can use the SDKCall.execute method to run code on the SDK thread.

SDKCall.execute {
// Your code here
}

This ensures that the code inside the block runs on the SDK thread, allowing safe interaction with SDK objects.

danger

If you perform SDK operations from a different thread the sdk will throw an exception: java.lang.Exception: Please ensure that your call is placed on GEMSdkThread or GLThread. See GemCall.execute{ ... }!

Units of measurement

The SDK primarily uses SI units for measurements, including:

  • Meter (distance).
  • Second (time).
  • Kilogram (mass).
  • Meters per second (speed).
  • Watts for power (consumption).

The length value and measurement unit used in TTS (text-to-speech) instructions, such as "After 150 meters, bear left," are determined by the unit system configured through the SDKSettings.unitSystem setter.

danger

In certain cases, other units of measurement are used as they are better suited. For instance, the height, length and width fields of the TruckProfile class are measured in centimeters. These exceptions are clearly indicated in the API reference.

Error handling

The Maps SDK for Android uses a standardized error handling system through the GemError class. All SDK operations return error codes that indicate the success or failure of the requested operation.

Understanding Error Codes

Error codes are represented as integers (type alias ErrorCode = Int). The SDK provides various error constants in the GemError object:

  • GemError.NoError (0): Operation completed successfully
  • GemError.Cancel (-3): Operation was canceled (not considered an error)
  • Negative values: Indicate various error conditions

Checking for Errors

Use GemError.isError() to determine if an error code represents an actual error:

val errorCode = someSDKOperation()
if (GemError.isError(errorCode)) {
// Handle the error
val errorMessage = GemError.getMessage(errorCode, context)
Log.e("SDK", "Operation failed: $errorMessage")
} else {
// Operation succeeded or was canceled
}

Getting Error Messages

The GemError.getMessage() method provides human-readable error descriptions:

val errorMessage = GemError.getMessage(errorCode, context)

Common Error Types

The SDK defines several categories of errors:

File and I/O Errors:

  • GemError.Io (-6): General I/O error
  • GemError.AccessDenied (-7): Access denied to resource
  • GemError.NotFound (-11): Required item not found
  • GemError.NoDiskSpace (-9): Insufficient disk space

Network Errors:

  • GemError.NoConnection (-24): No network connection available
  • GemError.NetworkTimeout (-29): Network operation timed out
  • GemError.NetworkFailed (-23): Network connection failed
  • GemError.ConnectionRequired (-25): Network connection required

Routing and Navigation Errors:

  • GemError.NoRoute (-18): No route could be calculated
  • GemError.WaypointAccess (-19): One or more waypoints not accessible
  • GemError.RouteTooLong (-20): Route is too long, use intermediate waypoints

Resource and Memory Errors:

  • GemError.NoMemory (-14): Insufficient memory
  • GemError.ResourceMissing (-36): Required resources missing, reinstall needed
  • GemError.InvalidInput (-15): Invalid input provided

Handling Asynchronous Operations

Many SDK operations are asynchronous and use listeners to report completion. Use ProgressListener to handle errors in async operations:

val listener = ProgressListener.create(
onCompleted = { errorCode, hint ->
if (GemError.isError(errorCode)) {
val errorMessage = GemError.getMessage(errorCode, this)
// Handle error appropriately
showErrorDialog("Operation failed: $errorMessage")
} else {
// Handle successful completion
onOperationSuccess()
}
}
)

Best Practices

  1. Always check error codes: Never assume operations succeed without checking the return value
  2. Provide user feedback: Convert technical error codes to user-friendly messages
  3. Handle network errors gracefully: Implement retry logic for network-related operations
  4. Log errors for debugging: Include error codes and messages in your application logs
  5. Handle specific error types: Implement different handling strategies for different error categories
// Example: Comprehensive error handling
fun handleSDKError(errorCode: ErrorCode, context: Context) {
when (errorCode) {
GemError.NoError -> {
// Success - no action needed
}
GemError.Cancel -> {
// Operation was canceled by user
showMessage("Operation canceled")
}
GemError.NoConnection,
GemError.NetworkFailed,
GemError.NetworkTimeout -> {
// Network-related errors
showRetryDialog("Network error. Please check your connection and try again.")
}
GemError.NoRoute -> {
// Routing specific error
showMessage("Could not calculate route. Please check your destination.")
}
GemError.ResourceMissing -> {
// Critical error requiring app reinstall
showMessage("App resources are missing. Please reinstall the application.")
}
else -> {
// Generic error handling
val message = GemError.getMessage(errorCode, context)
showMessage("Error: $message")
}
}
}

Do not extend the classes provided by the SDK

The SDK is designed to deliver all necessary functionalities in an intuitive and straightforward manner. Avoid extending any of the Maps SDK for Android classes. Use the callback methods provided by the SDK for seamless integration.

DateTime Parameter Rules

Make sure the DateTime passed to method is a valid positive utc value. Values below the DateTime.utc(0) are not supported.

  • Use the provided register methods or constructor parameters to subscribe to events.
  • Only one callback can be active per event type. If multiple callbacks are registered for the same event type, the most recently registered callback will override the previous one.
  • To unsubscribe from events, register an empty callback.

Provide SDK logs when reporting issues

danger

The logs may contain sensitive information, so review them before sharing publicly.

Collecting SDK logs and crash reports

The SDK automatically generates logs and crash reports that are essential for diagnosing issues. To access and send these logs:

Automatic log collection

The SDK automatically creates log files at runtime. You can access these logs using the GemSdk.appLogPath property, which returns the path to the current application log file.

Crash reports

If the application crashes, crash logs are automatically stored in the SDK's internal storage path under GMcrashlogs/last/. You can access the internal storage path using GemSdk.internalStoragePath.

Example implementation

Here's how to programmatically collect and send debug information (based on the SendDebugInfo example):

private fun sendDebugInfo() {
// Get SDK version for subject line
var subject = ""
SdkCall.execute {
subject = GemSdk.sdkVersion?.let {
String.format("User feedback (SDK) - %d.%d.%d.%d.%s",
it.major, it.minor, it.year, it.week, it.revision)
} ?: "User feedback"
}

// Create email intent
val sendIntent = Intent(Intent.ACTION_SEND_MULTIPLE)
sendIntent.type = "message/rfc822"
sendIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf("support@magicearth.com"))
sendIntent.putExtra(Intent.EXTRA_SUBJECT, subject)

val uris = ArrayList<Uri>()

// Attach application logs
val privateLogPath = GemSdk.appLogPath
privateLogPath?.let { logPath ->
val publicPath = GemUtil.getApplicationPublicFilesAbsolutePath(this, "phoneLog.txt")
if (GemUtil.copyFile(logPath, publicPath)) {
val file = File(publicPath)
file.deleteOnExit()
try {
uris.add(FileProvider.getUriForFile(this, packageName + ".provider", file))
} catch (e: Exception) {
GEMLog.error(this, "Error attaching log file: ${e.message}")
}
}
}

// Attach crash reports if available
if (GemSdk.internalStoragePath.isNotEmpty()) {
val crashPath = "${GemSdk.internalStoragePath}/GMcrashlogs/last"
val crashDir = File(crashPath)
if (crashDir.exists() && crashDir.isDirectory) {
crashDir.listFiles()?.firstOrNull()?.let { crashFile ->
try {
uris.add(FileProvider.getUriForFile(this, packageName + ".provider", crashFile))
} catch (e: Exception) {
GEMLog.error(this, "Error attaching crash file: ${e.message}")
}
}
}
}

sendIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
startActivity(sendIntent)
}

Required Android manifest configuration

To share log files, you need to configure a FileProvider in your AndroidManifest.xml:

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>

And create a res/xml/provider_paths.xml file:

<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="external_files" path="." />
<files-path name="files" path="." />
<cache-path name="cache" path="." />
</paths>

Adding debug logs to your code

You can add custom debug messages to help with troubleshooting:

GEMLog.debug(this, "This is a debug message!")
GEMLog.error(this, "This is an error message!")

Other useful information

When reporting issues, please provide the following information to help us diagnose and resolve the problem effectively:

  • Clear and concise description of the bug or issue.
  • Steps to reproduce the issue, including a minimal code sample if possible.
  • Expected behavior versus actual behavior you are experiencing.
  • Screenshots or videos to help visualize the issue (if applicable).
  • SDK version you are using (obtainable via GemSdk.sdkVersion).
  • Platform details (e.g., iOS/Android, OS version, device model).
  • SDK logs and crash reports (using the method described above).
  • Geographical location where the issue occurred, if relevant (for example, routing, navigation, or search-related issues).
  • Any other relevant information that could assist us in diagnosing and resolving the issue.
  • Additionally, ensure that your SDK is up-to-date, as newer versions often include bug fixes and performance improvements.