Many ride-hailing, fitness, and navigation apps are capable of tracking the user's real time location, and presenting it on a map display, offering a new realm of convenience for users.
In this codelab, we will take a closer look into how this is made possible, with user running used as an example, for the motion tracking capabilities in Location Kit and Map Kit, which you can integrate into your app with ease.
In this codelab, you will:
In this codelab, you will learn how to:
To integrate HMS Core kits, you need to complete the following preparations:
For details, please refer to Preparations for Integrating HUAWEI HMS Core.
You can enable Location Kit and Map Kit in either of the following methods:
Sign in to AppGallery Connect, click My projects, select the created project, and go to Project settings > Manage APIs.
Toggle on the Location Kit and Map Kit switches.
Sign in to HUAWEI Developers and click Console. Then, go to HMS API Services > API Library, and select your project.
Find the Location Kit and Map Kit cards on the API Library page.
Click the cards, and then click Enable to enable the kits.


Sign in to AppGallery Connect, click My projects, select your project, then go to Project settings > General information, and click Set located next to Data storage location.
Download the agconnect-services.json file in the App information area.
Save the agconnect-services.json file to the app directory in your project.
Open the build.gradle file in the root directory for your project.
The code in the project-level build.gradle file is as follows:
buildscript {
    ext.kotlin_version = "1.4.10"
    repositories {
        google()
        jcenter()
        maven {url 'http://developer.huawei.com/repo/'}
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.1.1"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.huawei.agconnect:agcp:1.4.0.300'
        // NOTE: Do not place your app dependencies here; they belong
        // in the individual module build.gradle files
    }
}
allprojects {
    repositories {
        google()
        jcenter()
        maven {url 'http://developer.huawei.com/repo/'}
    }
}
Open the build.gradle file in the app directory for your project.
Add the dependencies on Location Kit and Map Kit in the dependencies section.
dependencies {
    implementation 'com.huawei.hms:location:{version}'
    implementation 'com.huawei.hms:maps:{version}'
}
Click Sync Now to synchronize the project.
-ignorewarnings 
-keepattributes *Annotation* 
-keepattributes Exceptions 
-keepattributes InnerClasses 
-keepattributes Signature 
-keepattributes SourceFile,LineNumberTable 
-keep class com.huawei.hianalytics.**{*;} 
-keep class com.huawei.updatesdk.**{*;} 
-keep class com.huawei.hms.**{*;}
"R.string.hms*", 
"R.string.connect_server_fail_prompt_toast", 
"R.string.getting_message_fail_prompt_toast", 
"R.string.no_available_network_prompt_toast", 
"R.string.third_app_*", 
"R.string.upsdk_*", 
"R.layout.hms*", 
"R.layout.upsdk_*", 
"R.drawable.upsdk*", 
"R.color.upsdk*", 
"R.dimen.upsdk*", 
"R.style.upsdk*",
"R.string.agc*"
Map Kit is an SDK for map development that covers map data in more than 200 countries and regions, and offers support in over 100 different languages. It is designed to help you integrate map-based functions into your app with ease.
We will now use Map Kit to draw a basic map.
The HMS Core Map SDK currently supports two types of map containers: SupportMapFragment and MapView.
MapView is a subclass of the Android View class, and can be used to place a map within an Android view. Similar to SupportMapFragment, MapView serves as a map container, and displays core map functions via a HuaweiMap object. To use the MapView class when calling APIs in normal interaction mode, you need to call the following methods in the corresponding methods for your Activity/Fragment: onCreate(), onStart(), onResume(), onPause(), onStop(), onDestroy(), onSaveInstanceState(Bundle outState), and onLowMemory().
In this codelab, we will use MapView to create a map sample.
Create a MapActivity class, add a MapView object in the layout file, and set the map attributes in the file.
Layout sample code:
<com.huawei.hms.maps.MapView
    android:id="@+id/hw_mapview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    map:mapType="normal"
    map:uiCompass="true"
    map:uiZoomControls="true"/>
In MapActivity, you need to call the OnMapReadyCallback API, and declare the MapView, HuaweiMap, as well as PolylineOptions (for drawing a polyline on the map) objects.
Java:
public class MapActivity extends AppCompatActivity implements OnMapReadyCallback {
    private MapView mMapView;
    private HuaweiMap mHwMap;
    private PolylineOptions mPolylineOptions;
    private LocationSource.OnLocationChangedListener mListener;
}
Kotlin:
class MapActivity : AppCompatActivity(), OnMapReadyCallback {
    private lateinit var mMapView: MapView
    private var mHwMap: HuaweiMap? = null
    private var mPolylineOptions: PolylineOptions? = null
    private var mListener: LocationSource.OnLocationChangedListener? = null
}
In MapActivity, load MapView in the onCreate() method, and call getMapAsync() to register the callback.
Java:
// init MapView
mMapView = findViewById(R.id.hw_mapview);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
    mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY);
}
mMapView.onCreate(mapViewBundle);
mMapView.getMapAsync(this);
Kotlin:
// init MapView
mMapView = findViewById(R.id.hw_mapview)
var mapViewBundle: Bundle? = null
if (savedInstanceState != null) {
    mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY)
}
mMapView.onCreate(mapViewBundle)
mMapView.getMapAsync(this)
Java:
@Override
public void onMapReady(HuaweiMap huaweiMap) {
    mHwMap = huaweiMap;
}
Kotlin:
override fun onMapReady(huaweiMap: HuaweiMap?) {
    mHwMap = huaweiMap
}
Call the MapView's methods corresponding to onStart(), onResume(), onPause(), onStop(), onDestroy(), and onLowMemory().
Java:
@Override
protected void onStart() {
    super.onStart();
    mMapView.onStart();
}
@Override
protected void onResume() {
    super.onResume();
    mMapView.onResume();
}
@Override
protected void onPause() {
    super.onPause();
    mMapView.onPause();
}
@Override
protected void onStop() {
    super.onStop();
    mMapView.onStop();
}
@Override
protected void onDestroy() {
    super.onDestroy();
    mMapView.onDestroy();
}
@Override
public void onLowMemory() {
    super.onLowMemory();
    mMapView.onLowMemory();
}
Kotlin:
companion object {
    private const val MAPVIEW_BUNDLE_KEY = "MapViewBundleKey"
}
override fun onStart() {
    super.onStart()
    mMapView.onStart()
}
override fun onResume() {
    super.onResume()
    mMapView.onResume()
}
override fun onPause() {
    super.onPause()
    mMapView.onPause()
}
override fun onStop() {
    super.onStop()
    mMapView.onStop()
}
override fun onDestroy() {
    super.onDestroy()
    mMapView.onDestroy()
}
override fun onLowMemory() {
    super.onLowMemory()
    mMapView.onLowMemory()
}
Now that we have created a basic map, you can get a sense of how it looks, as seen below:
Location Kit incorporates the GNSS, Wi-Fi, and base station location functionalities into your app to bolster global positioning capabilities, allowing you to offer flexible location-based services that meet the needs of a global user base. The kit currently provides three main capabilities: fused location, activity identification, and geofence.
In this codelab, we'll be using the fused location function.
<!-- Location kit needs location permission-->
<uses-permission android:name="android.permission.ACCESS_COARES_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<!--    Use the mock location data-->
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
 
In this codelab, the mock location function is used. Therefore, the ACCESS_MOCK_LOCATION permission is assigned.
The following is in accordance with the requirements for dangerous permissions in Android 6.0.
Java:
// Dynamically apply for required permissions if the API level is 28 or smaller.
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
    Log.i(TAG, "sdk <= 28 Q");
    if (ActivityCompat.checkSelfPermission(this,
            Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
            && ActivityCompat.checkSelfPermission(this,
            Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        String[] strings =
                {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
        ActivityCompat.requestPermissions(this, strings, 1);
    }
} else {
    // Dynamically apply for required permissions if the API level is greater than 28. The android.permission.ACCESS_BACKGROUND_LOCATION permission is required.
    if (ActivityCompat.checkSelfPermission(this,
            Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
            && ActivityCompat.checkSelfPermission(this,
            Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
            && ActivityCompat.checkSelfPermission(this,
            "android.permission.ACCESS_BACKGROUND_LOCATION") != PackageManager.PERMISSION_GRANTED) {
        String[] strings = {android.Manifest.permission.ACCESS_FINE_LOCATION,
                android.Manifest.permission.ACCESS_COARSE_LOCATION,
                "android.permission.ACCESS_BACKGROUND_LOCATION"};
        ActivityCompat.requestPermissions(this, strings, 2);
    }
}
Kotlin:
// Dynamically apply for required permissions if the API level is 28 or smaller.
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
    Log.i(TAG, "sdk <= 28 Q")
    if (ActivityCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) != PackageManager.PERMISSION_GRANTED
        && ActivityCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_COARSE_LOCATION
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        val strings = arrayOf(
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_COARSE_LOCATION
        )
        ActivityCompat.requestPermissions(this, strings, 1)
    }
} else {
    // // Dynamically apply for required permissions if the API level is greater than 28. The android.permission.ACCESS_BACKGROUND_LOCATION permission is required.
    if (ActivityCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_COARSE_LOCATION
        ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_BACKGROUND_LOCATION
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        val strings = arrayOf(
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_COARSE_LOCATION,
            Manifest.permission.ACCESS_BACKGROUND_LOCATION
        )
        ActivityCompat.requestPermissions(this, strings, 2)
    }
}
Java:
protected void onCreate(@Nullable Bundle savedInstanceState) {
    fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
}
Kotlin:
override fun onCreate(savedInstanceState: Bundle?) {
    fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
}
Location Kit also provides a function for checking the device location settings. First, create a checkLocationSettings() method. Your app can obtain the SettingsClient instance using getSettingsClient(Activity activity) for LocationServices, and call the checkLocationSettings(LocationSettingsRequest locationSettingsRequest) API to obtain the device location settings.
Java:
private void checkLocationSettings() {
    LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
    LocationSettingsRequest locationSettingsRequest = builder.build();
    SettingsClient settingsClient = LocationServices.getSettingsClient(this);
    settingsClient.checkLocationSettings(locationSettingsRequest)
            .addOnSuccessListener(new OnSuccessListener<LocationSettingsResponse>() {
                @Override
                public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
                    requestLocationUpdate();
                }
            }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(Exception e) {
            int statusCode = ((ApiException) e).getStatusCode();
            switch (statusCode) {
                case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                    try {
                        ResolvableApiException rae = (ResolvableApiException) e;
                        rae.startResolutionForResult(MapActivity.this, 0);
                    } catch (IntentSender.SendIntentException sie) {
                    }
                    break;
            }
        }
    });
}
Kotlin:
private fun checkLocationSettings() {
    val builder: LocationSettingsRequest.Builder = LocationSettingsRequest.Builder()
    val locationSettingsRequest: LocationSettingsRequest = builder.build()
    val settingsClient: SettingsClient = LocationServices.getSettingsClient(this)
    settingsClient.checkLocationSettings(locationSettingsRequest)
        .addOnSuccessListener { requestLocationUpdate() }.addOnFailureListener { e ->
            when ((e as ApiException).statusCode) {
                LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
                    val rae: ResolvableApiException = e as ResolvableApiException
                    rae.startResolutionForResult(this@MapActivity, 0)
                } catch (sie: SendIntentException) {
                }
            }
        }
}
If you would like to continually locate the device, you can use the requestLocationUpdates() API provided by Location Kit to do so. This API returns a LocationResult object containing the location information by calling the onLocationResult() method in the defined LocationCallback class.
Java:
private void requestLocationUpdate() {
    mLocationRequest = new LocationRequest();
    mLocationRequest.setInterval(5000);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    mLocationCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
            super.onLocationResult(locationResult);
            writeGpsData2Sdcard(locationResult.getLastLocation());
            if (mIsRunning) {
                updateLocation(locationResult.getLastLocation());
            }
        }
        @Override
        public void onLocationAvailability(LocationAvailability locationAvailability) {
            super.onLocationAvailability(locationAvailability);
        }
    };
    fusedLocationProviderClient
            .requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.getMainLooper())
            .addOnSuccessListener(new OnSuccessListener<Void>() {
                @Override
                public void onSuccess(Void aVoid) {
                    Log.i(TAG, "request location updates success");
                }
            })
            .addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(Exception e) {
                    Log.e(TAG, "request location updates failed, error: " + e.getMessage());
                }
            });
}
Kotlin:
private fun requestLocationUpdate() {
    mLocationRequest = LocationRequest()
    mLocationRequest!!.interval = 5000
    mLocationRequest!!.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
    mLocationCallback = object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult) {
            super.onLocationResult(locationResult)
            writeGpsData2Sdcard(locationResult.lastLocation)
            if (mIsRunning) {
                processLocationChange(locationResult.lastLocation)
            }
        }
        override fun onLocationAvailability(locationAvailability: LocationAvailability?) {
            super.onLocationAvailability(locationAvailability)
        }
    }
    fusedLocationProviderClient
        ?.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.getMainLooper())
        ?.addOnSuccessListener { Log.i(TAG, "request location updates success") }
        ?.addOnFailureListener { e ->
            Log.e(
                TAG,
                "request location updates failed, error: " + e.message
            )
        }
}
Stop requesting location updates when it is no longer required, to reduce power consumption. To do this, call removeLocationUpdates() and pass LocationCallback that you have passed to the requestLocationUpdates() API.
Java:
private void removeLocationUpdatesWithCallback() {
    try {
        Task<Void> voidTask = fusedLocationProviderClient.removeLocationUpdates(mLocationCallback);
        voidTask.addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(Exception e) {
            }
        });
    } catch (Exception e) {
    }
}
Kotlin:
private fun removeLocationUpdatesWithCallback() {
    try {
        val voidTask: Task<Void> =
            fusedLocationProviderClient!!.removeLocationUpdates(mLocationCallback)
        voidTask.addOnSuccessListener { }.addOnFailureListener { }
    } catch (e: Exception) {
    }
}
    In the steps above, we have implemented the basic functions in Map Kit and Location Kit. This section will detail how to use Map Kit and Location Kit to record the user running track, as well as calculate the running distance and speed.
Add the location source to the OnMapReady callback.
Java:
// Add the location source.
mHwMap.setLocationSource(new LocationSource() {
    @Override
    public void activate(OnLocationChangedListener onLocationChangedListener) {
        mListener = onLocationChangedListener;
    }
    @Override
    public void deactivate() {
    }
});
Kotlin:
// Add the location source.
mHwMap?.setLocationSource(object : LocationSource {
    override fun activate(onLocationChangedListener: LocationSource.OnLocationChangedListener?) {
        mListener = onLocationChangedListener
    }
    override fun deactivate() {}
})
In the OnMapReady callback, add the code for obtaining the location, and update the map camera.
Java:
// Obtain the current location and update the map camera.
try {
    Task<Location> lastLocation = fusedLocationProviderClient.getLastLocation();
    lastLocation.addOnSuccessListener(new OnSuccessListener<Location>() {
        @Override
        public void onSuccess(Location location) {
            mListener.onLocationChanged(location);
            if (mListener != null) {
                mListener.onLocationChanged(location);
                CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(
                        new LatLng(location.getLatitude(), location.getLongitude()), 15f);
                mHwMap.animateCamera(cameraUpdate);
            }
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(Exception e) {
        }
    });
} catch (Exception e) {
}
Kotlin:
// Obtain the current location and update the map camera.
try {
    val lastLocation: Task<Location> = fusedLocationProviderClient!!.lastLocation
    lastLocation.addOnSuccessListener { location ->
        mListener!!.onLocationChanged(location)
        if (mListener != null) {
            mListener!!.onLocationChanged(location)
            val cameraUpdate: CameraUpdate = CameraUpdateFactory.newLatLngZoom(
                LatLng(location.latitude, location.longitude), 15f
            )
            mHwMap!!.animateCamera(cameraUpdate)
        }
    }.addOnFailureListener {
        Log.d(TAG, "onMapReady: Obtains the current position failure")
    }
} catch (e: Exception) {
}
After the location source is set, the current location can be displayed on the map, as shown in the figure below.
First, add a layout for displaying motion data.
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:layout_marginTop="60dp"
    android:layout_marginStart="15dp"
    android:layout_marginEnd="15dp"
    android:background="@color/bgWhite"
    android:orientation="horizontal">
    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:layout_marginStart="20dp"
        android:layout_marginTop="20dp"
        android:orientation="vertical">
        <TextView
            android:id="@+id/tv_distance"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:gravity="center_horizontal"
            android:maxLength="8"
            android:text="0.00"
            android:textColor="#000000"
            android:textSize="25sp"
            android:textStyle="bold" />
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:layout_marginStart="6dp"
            android:layout_marginBottom="2.5dp"
            android:text="km"
            android:textColor="#88000000"
            android:textSize="18sp" />
    </LinearLayout>
Add the motion data display UI in MapActivity.
Java:
// Add the motion data display UI.
        mTvSpeed = findViewById(R.id.tv_speed);
        mTvDistance = findViewById(R.id.tv_distance);
        mTime = findViewById(R.id.cm_time);
        mTvStart = findViewById(R.id.tv_start);
        mTvStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                processStartClick();
            }
        });
Kotlin:
// Add the motion data display UI.
mTvSpeed = findViewById(R.id.tv_speed)
mTvDistance = findViewById(R.id.tv_distance)
mTime = findViewById(R.id.cm_time)
mTvStart = findViewById(R.id.tv_start)
mTvStart!!.setOnClickListener(View.OnClickListener { processStartClick() })
Add the following code to the processStartClick() method, which will be called when the user starts running:
Java:
mIsRunning = true;
mPath.reset();
mPath.setStartTime(System.currentTimeMillis());
mHandler.post(mTimeRunnable);
mTvStart.setText("Stop");
Kotlin:
mIsRunning = true
mPath.reset()
mPath.startTime = System.currentTimeMillis()
mHandler.post(mTimeRunnable)
mTvStart!!.text = "Stop"
Add the following code to the processStartClick() method, which will be called when the user stops running:
Java:
mIsRunning = false;
mPath.setEndTime(System.currentTimeMillis());
mTvStart.setText("Start");
mHandler.removeCallbacks(mTimeRunnable);
if (mPath.getPathLine().size() > 0) {
    mPath.setEndPoint(mPath.getPathLine().get(mPath.getPathLine().size() - 1));
    if (null != mMarkerStart && null != mMarkerEnd) {
        mMarkerStart.remove();
        mMarkerEnd.remove();
    }
    MarkerOptions StartPointOptions = new MarkerOptions()
            .icon(BitmapDescriptorFactory.fromResource(R.mipmap.map_marker_bubble_azure_small))
            .position(mPath.getStartPoint());
    StartPointOptions.title("Start Point");
    StartPointOptions.snippet("Start Point");
    mMarkerStart = mHwMap.addMarker(StartPointOptions);
    MarkerOptions EndPointOptions = new MarkerOptions()
            .icon(BitmapDescriptorFactory.fromResource(R.mipmap.map_marker_bubble_azure_small))
            .position(mPath.getEndPoint());
    EndPointOptions.title("End Point");
    EndPointOptions.snippet("End Point");
    mMarkerEnd = mHwMap.addMarker(EndPointOptions);
}
Kotlin:
mIsRunning = false
mPath.endTime = (System.currentTimeMillis())
mTvStart!!.text = "Start"
mHandler.removeCallbacks(mTimeRunnable)
if (mPath.mPathLinePoints!!.size > 0) {
    mPath.endPoint = (mPath.mPathLinePoints!!.get(mPath.mPathLinePoints!!.size - 1))
    if (null != mMarkerStart && null != mMarkerEnd) {
        mMarkerStart!!.remove()
        mMarkerEnd!!.remove()
    }
    val startPointOptions: MarkerOptions = MarkerOptions()
        .icon(BitmapDescriptorFactory.fromResource(R.mipmap.map_marker_bubble_azure_small))
        .position(mPath.mStartPoint)
    startPointOptions.title("Start Point")
    startPointOptions.snippet("Start Point")
    mMarkerStart = mHwMap!!.addMarker(startPointOptions)
    val endPointOptions: MarkerOptions = MarkerOptions()
        .icon(BitmapDescriptorFactory.fromResource(R.mipmap.map_marker_bubble_azure_small))
        .position(mPath.mEndPoint)
    endPointOptions.title("End Point")
    endPointOptions.snippet("End Point")
    mMarkerEnd = mHwMap!!.addMarker(endPointOptions)
}
Bind the processStartClick() method to the Start button.
Create the processLocationChange(Location location) method, which will draw a polyline on the map based on location updates.
Java:
private void processLocationChange(Location location) {
    LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
    // Draw a motion track on a map.
    mPolylineOptions.add(latLng);
    mHwMap.addPolyline(mPolylineOptions);
    // Update the Map Kit camera.
    if (mListener != null) {
        mListener.onLocationChanged(location);
        CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(
                new LatLng(location.getLatitude(), location.getLongitude()), 15f);
        mHwMap.animateCamera(cameraUpdate);
    }
}
Kotlin:
private fun processLocationChange(location: Location) {
    val latLng = LatLng(location.latitude, location.longitude)
    if (mPath.mStartPoint == null) {
        mPath.mStartPoint =  latLng
    }
    mPath.addPoint(latLng)
    val distance: Float = mPath.updateDistance()
    val sportMile = distance / 1000.0
    if (mSeconds > 0) {
        val distribution = mSeconds.toDouble() / 60.0 / sportMile
        mPath.setDistribution(distribution)
        mTvSpeed!!.text = mDecimalFormat.format(distribution)
        mTvDistance!!.text = mDecimalFormat.format(sportMile)
    } else {
        mPath.setDistribution(0.0)
        mTvSpeed!!.text = "0.00"
        mTvDistance!!.text = "0.00"
    }
    // Draw a motion track on a map.
    mPolylineOptions!!.add(latLng)
    mHwMap!!.addPolyline(mPolylineOptions)
    // Update the Map Kit camera.
    if (mListener != null) {
        mListener!!.onLocationChanged(location)
        val cameraUpdate: CameraUpdate = CameraUpdateFactory.newLatLngZoom(
            LatLng(location.latitude, location.longitude), 15f
        )
        mHwMap!!.animateCamera(cameraUpdate)
    }
}
Call the processLocationChange() method in the requestLocationUpdate() method.
Java:
private void requestLocationUpdate() {
    mLocationCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
            super.onLocationResult(locationResult);
            if (mIsRunning) {
                processLocationChange(locationResult.getLastLocation());
            }
        }
    };
Kotlin:
mLocationCallback = object : LocationCallback() {
    override fun onLocationResult(locationResult: LocationResult) {
        super.onLocationResult(locationResult)
        writeGpsData2Sdcard(locationResult.lastLocation)
        if (mIsRunning) {
            processLocationChange(locationResult.lastLocation)
        }
    }
    override fun onLocationAvailability(locationAvailability: LocationAvailability?) {
        super.onLocationAvailability(locationAvailability)
    }
}
Finally, the app can draw the motion track based on location updates that are obtained continuously.
Location Kit provides the mock location function, which can be used for app debugging purposes.
Go to Developer options > Select mock location app on the test device and select a specific app.
Obtain a set of mock GPS data. In this codelab, the Java GPS data being used, is stored in MockGpsData.java, and the Kotlin GPS data is stored in MockGpsData.kt.
Before creating a map and obtaining the location, add the following code for loading the mock GPS data.
Java:
Task<Void> voidTask = fusedLocationProviderClient.setMockMode(true);
voidTask.addOnSuccessListener(new OnSuccessListener<Void>() {
    @Override
    public void onSuccess(Void aVoid) {
        Toast.makeText(MainActivity.this, "setMockMode onSuccess", Toast.LENGTH_SHORT).show();
        mHandler.post(new Runnable() {
            int point = 0;
            @Override
            public void run() {
                if (point + 1 >= MockGpsData.POINTS.length) {
                    return;
                }
                double latitude = MockGpsData.POINTS[point++];
                double longitude = MockGpsData.POINTS[point++];
                final Location mockLocation = new Location(LocationManager.GPS_PROVIDER);
                mockLocation.setLongitude(longitude);
                mockLocation.setLatitude(latitude);
                Task<Void> voidTask = fusedLocationProviderClient.setMockLocation(mockLocation);
                voidTask.addOnSuccessListener(new OnSuccessListener<Void>() {
                    @Override
                    public void onSuccess(Void aVoid) {
                    }
                }).addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(Exception e) {
                    }
                });
                mHandler.postDelayed(this, 100);
            }
        });
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(Exception e) {
        Toast.makeText(MainActivity.this, "setMockMode onFailure:" + e.getMessage(), Toast.LENGTH_SHORT).show();
    }
});
Kotlin:
val voidTask: Task<Void> = fusedLocationProviderClient!!.setMockMode(true)
voidTask.addOnSuccessListener(object : OnSuccessListener<Void?> {
    override fun onSuccess(aVoid: Void?) {
        Toast.makeText(this@MainActivity, "setMockMode onSuccess", Toast.LENGTH_SHORT)
            .show()
        mHandler.post(object : Runnable {
            var point = 0
            override fun run() {
                if (point + 1 >= MockGpsData.POINTS.size) {
                    return
                }
                val latitude: Double = MockGpsData.POINTS.get(point++)
                val longitude: Double = MockGpsData.POINTS.get(point++)
                val mockLocation = Location(LocationManager.GPS_PROVIDER)
                mockLocation.longitude = longitude
                mockLocation.latitude = latitude
                val voidTask: Task<Void> =
                    fusedLocationProviderClient!!.setMockLocation(mockLocation)
                voidTask.addOnSuccessListener(object : OnSuccessListener<Void?> {
                    override fun onSuccess(aVoid: Void?) {}
                }).addOnFailureListener(object : OnFailureListener {
                    override fun onFailure(e: Exception?) {}
                })
                mHandler.postDelayed(this, 100)
            }
        })
    }
}).addOnFailureListener { e ->
    if (e != null) {
        Toast.makeText(
            this@MainActivity,
            "setMockMode onFailure:" + e.message,
            Toast.LENGTH_SHORT
        ).show()
    }
}
After the mock GPS data is loaded, you can use it for debugging.
Well done. You have successfully completed this codelab, and learned how to:
For more information, please visit the following:
You can also click the button below to download the source code.