Quick Start

Caution

The SDK library is always improving. For the latest developments and features please check Release Notes.

Minimal requirements

  • Android Studio
  • Android device or emulator running API 23 and above
  • Since this is an offline maps-based SDK, sufficient space on internal or external storage is required

Adding the library to your project

Please check Release Notes to find out the current version of the library.

Add this piece of code to settings.gradle

dependencyResolutionManagement {
  ..
  repositories {
    ..
        maven {
            url "https://sdk.mapfactor.com/repo/release"
            credentials {
                username "$mavenUsername"
                password "$mavenPassword"
            }
        }
  }
}

or build.gradle for older projects

allprojects {
  repositories {
    ..
      maven {
          url "https://sdk.mapfactor.com/repo/release"
          credentials {
              username "$mavenUsername"
              password "$mavenPassword"
          }
      }
  }
}

Edit your gradle.properties

mavenUsername=askForUsername
mavenPassword=askForPassword

Username and password will be provided by our sales department.

Configure your build.gradle

dependencies {
    implementation "com.mapfactor:sdk:x.y.z"
}

Adding to the layout

Add implementation 'com.google.android.material:material:x.y.z' for back compatibility. Use FragmentActivity instead of a ComponentActivity successor to support our Fragment.

Jetpack Compose

In case of using Jetpack Compose, check Inter-op with XML inter-op docs by Google. Insertion to the layout can be executed either by view or fragment. Our MapView is basically a view containing MapFragment.

MapView element

View plays the role of a wrapper around our MapFragment. Once MapView is inflated, you can access the inner fragment.

AndroidView(
    modifier = Modifier.fillMaxSize(),
    factory = { context ->
        MapView(context).apply {
            //do whatever you want, e.g.

            //set night mode
            this.mapFragment.setNightMode(true)

            //handle map controls visibility
            val mapControls = this.mapFragment.mapControls
            mapControls.setMapControlVisibility(
                MapControls.MapControl.COMPASS,
                true
            )

            //or add listener
            this.mapFragment.addOnMapReadyListener {

            }
        }
    },
    update = {
    }
)

Fragment element

You need to create an XML layout file in res/layout. e.g. fragment_navigation.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragment_container_view"
        android:name="com.mapfactor.sdk.map.MapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

and then use this layout to inflate in Kotlin like this.

AndroidViewBinding(FragmentNavigationBinding::inflate) {
    val mapFragment = fragmentContainerView.getFragment<MapFragment>()

    //do whatever you want, e.g.

    //set night mode
    mapFragment.setNightMode(true)

    //handle map controls visibility
    val mapControls = mapFragment.mapControls
    mapControls.setMapControlVisibility(
         MapControls.MapControl.COMPASS,
         true
    )

    //or add listener
    mapFragment.addOnMapReadyListener {

    }
}

For more information, check official Google documentation.

View-based system

For layout implementation, there are two options - direct XML view containing MapFragment on background or inserting a MapFragment with FragmentContainerView.

MapView element

<com.mapfactor.sdk.map.MapView
      android:id="@+id/map_view"
      android:layout_width="match_parent"
      android:layout_height="match_parent"/>

Fragment element

<androidx.fragment.app.FragmentContainerView
      android:id="@+id/map_fragment"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:name="com.mapfactor.sdk.map.MapFragment"/>

Initialization

To ensure our SDK works properly, add dependencies to a build.gradle:

implementation "androidx.compose.ui:ui-viewbinding:x.y.z"
implementation "androidx.fragment:fragment-ktx:x.y.z"
implementation 'androidx.constraintlayout:constraintlayout:x.y.z'

buildFeatures {
  viewBinding true
}

You will need to add permissions to access device location to AndroidManifest.xml as follows.

<manifest..>
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<manifest>

The key to run our SDK is MpfcEngine’s init​ method.

// get path
val sdkDataPath = getExternalFilesDir(null).toString()

// get renderer
val rendererName: RendererName = RendererName.HARDWARE

// get map data provider
val mapDataProvider: ProviderName = ProviderName.OSM

//get language
val lang = "en"

MpfcEngine.getInstance().init(this, sdkDataPath, mapDataProvider, rendererName, lang, object :
    MpfcEngine.InitListener {
    override fun onLocationPermissionNotGranted() {
      // notify an activity to make a request for location permission,
      // e.g. using broadcast intent
    }

    override fun onEngineInitStatusChanged(initStatus: MpfcEngine.InitStatus) {
    // log
    }

    override fun onEngineInitFinished(initStatusResult: MpfcEngine.InitResult) {
      if (result == MpfcEngine.InitResult.SUCCESS) {
        //HURAYYY, do whatever you want, e.g.
          initActiveVehicle()
          getAllAvailableVehicles()
          setCurrentLanguage()
      } else if (result == MpfcEngine.InitResult.FAILED_DEVICE_NOT_ACTIVATED
          || result == MpfcEngine.InitResult.FAILED_SDK_NOT_LICENSED) {
         //oops, activate device
          activateDevice()
      }
    }
})

fun activateDevice() {
  MpfcEngine.getInstance().activateDevice(
    getApplication(),
    "AAAAA-BBBBB-CCCCC-DDDDD-EEEEE"
    )  { activationResult: MpfcEngine.ActivationResult ->
          val succeeded =
            activationResult == MpfcEngine.ActivationResult.SUCCEEDED ||
            activationResult == MpfcEngine.ActivationResult.ALREADY_ACTIVATED
            ...
        }
}

License key will be provided by our sales department.

From now on theMpfcEngine instance is used for most use cases.

Data download

For downloading map, voice and sound data, use the downloadAppData method of the AppDataManagerModule.

MpfcEngine.getInstance().appDataManagerModule
  .downloadAppData(dataIds, object : DataDownloadListener {
      override fun onDownloadProgress(
          dataId: String,
          dataName: String,
          iFile: Int,
          numFiles: Int,
          curFileProgress: Long,
          curFileSize: Long,
          totProgress: Long,
          totSize: Long
      ) {
          //process current progress
      }

      override fun onDownloadFinished() {

      }

      override fun onDownloadFailed(errorCode: AppDataManager.DownloadErrorCode) {
          //handle error
      }

      override fun onEngineRestarted() {

      }
  })

TIP: If using Kotlin, you can take advantage of callbackFlow.

Beware of executing methods related to MpfcEngine instance repeatedly. You can call them again once onEngineRestarted callback is invoked.