Overview

Map Kit is the most common service in lifestyle apps. In this application, a map kit was used to track taxis and routes. The user location can be displayed on the map, the desired destination can be selected on the map, icons can be added to the desired location (nearby taxis are shown.), the route and the movement on the route can be displayed on the map. These features were included in the application.

The destination and starting point of the route can be determined with the site kit. This process was achieved by using the place search feature of the Site Kit in the application. Site Kit allows your app to provide users convenient yet secure access to diverse location-based services. Also, the user location can be provided with the Location Kit to set as the route start point. Location Kit allows you to provide flexible location-based services for global users. The user's movement on the route can be tracked with the location kit and its progress can be displayed on the map. In this application, we made the progress on the route as an animation to be an example for visuality.

To use those kits, you will need to:

What You Will Need

Hardware Requirements

Software Requirements

Required Knowledge

What You Will Learn

In this codelab, you will learn how to:

Create an app in AppGallery Connect and obtain the project configuration file agconnect-services.json. The procedure is as follows:

For details, please refer to Preparations for Integrating HUAWEI HMS Core

You can download the sample code for HiTaxi at GitHub.

Note: You need to register as a developer to complete the operations above.

Enabling Kits and Services

  1. Sign in to AppGallery Connect, click My Projects, find your project, and click your desired app. On the page that is displayed, go to Project settings > Manage APIs.
  2. Toggle on the switches for Map Kit, Site Kit anLocation Kit.**t.

Integrating the HMS Core SDK

For Android Studio, Huawei provides the HMS Core SDK that can be integrated by using the Maven repository. Before developing your app, you will need to integrate the HMS Core SDK into your Android Studio project.

  1. Sign in to AppGallery Connect, click My Projects, find your project, and click your desired app. On the page that is displayed, go to Project settings > General information.
  2. In the App information area, click agconnect-services.json to download the configuration file.
  3. Copy the agconnect-service.json file to the project root to the app directory of your Android Studio project.
  4. Open the Android Studio project-level build.gradle file.
  5. In the build.gradle file of your Android Studio project, add the configurations.
    allprojects { repositories { google() jcenter() maven {url 'https://developer.huawei.com/repo/'} } } buildscript { repositories { google() jcenter() maven {url 'https://developer.huawei.com/repo/'} } } buildscript { dependencies { classpath 'com.android.tools.build:gradle:4.1.2' classpath 'com.huawei.agconnect:agcp:1.4.2.300' } }
  6. Open the Android Studio app-level build.gradle file.
  7. In the build.gradle file in the app directory of your Android Studio project, add the configurations.<
    dependencies { implementation 'com.huawei.hms:site:{version}' implementation 'com.huawei.hms:maps:{version}' implementation 'com.huawei.hms:location:{version}' implementation 'com.huawei.agconnect:agconnect-core:{version}' }
  8. Click Sync Now to synchronize the configurations.

In this codelab, we will create a Splash Page to check if location services are enabled by phone. We will create a page for requesting permission to use location services in the app. Also, we will create a page with a map to display the map, locations, routes, and nearby taxis. This page will be the application's main page. We will add a search bar for Site Kit and we will add a bottom sheet dialog to choosing taxi types, displaying ETA, estimated charge, and driver's information.

Step 1: Since this app is designed to provide users with ease-of-use in their travels, the app includes a location search feature which is included in the Site Kit. Please refer to Site Kit documentation for more information on how to integrate. Site Kit Location Search is accessed through Intent and we need to catch its results.

Kotlin:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initSearchIntent() } private fun initSearchIntent() { searchIntent = SearchIntent() searchIntent.setApiKey(URLEncoder.encode(BuildConfig.API_KEY, "UTF-8")) }

Java:

@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); initSearchIntent(); } private void initSearchIntent() { searchIntent = SearchIntent(); searchIntent.setApiKey(URLEncoder.encode(BuildConfig.API_KEY, "UTF-8")); }

Step 2: Start Search Service on a related action. In this case, a search bar click.

Kotlin:

fragmentViewBinding.searchLayout.setOnClickListener { showSiteSearchFragment() }

Java:

fragmentViewBinding.searchLayout.setOnClickListener(view -> { showSiteSearchFragment(); });

Kotlin:

private fun showSiteSearchFragment() { val intent = searchIntent.getIntent(requireActivity()) startActivityForResult(intent, SearchIntent.SEARCH_REQUEST_CODE) }

Java:

private void showSiteSearchFragment(){ Intent intent = searchIntent.getIntent(requireActivity()); startActivityForResult(intent, SearchIntent.SEARCH_REQUEST_CODE); }

Step 3: Set the onActivityResult method to catch the search results. The search bar is also updated when the Search Result is retrieved.

Kotlin:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (SearchIntent.SEARCH_REQUEST_CODE == requestCode) { if (SearchIntent.isSuccess(resultCode)) { val site = searchIntent.getSiteFromIntent(data) val latLng = LatLng(site.location.lat, site.location.lng) huaweiMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 18f)) saveAndShowAddress(latLng) } } } private fun saveAndShowAddress(latLng: LatLng) { fragmentViewBinding.searchAddressText.text = homeViewModel.getAddress(latLng) }

Java:

@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if(SearchIntent.SEARCH_REQUEST_CODE == requestCode){ Site site = searchIntent.getSiteFromIntent(data); LatLng latLng = new LatLng(site. location.lat, site.location.lng); huaweiMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 18f)); saveAndShowAddress(latLng); } } private void saveAndShowAddress(LatLng latLng) { fragmentViewBinding.searchAddressText.setText(homeViewModel.getAddress(latLng)); }

Step 1: We need to add a map view in our layout first and give it an id to use in our classes.

<com.huawei.hms.maps.MapView android:id="@+id/huaweiMap" android:layout_width="match_parent" android:layout_height="match_parent" map:cameraTargetLat="41.009314" map:cameraTargetLng="29.003253" map:cameraZoom="10" map:mapType="normal" map:uiCompass="true" map:uiZoomControls="true" />

Step 2: To initialize our map, we need to call getMapAsync() method after our view is created.

Kotlin:

override fun setupUi() { super.setupUi() lottieDialog = LottieDialog.Builder().setContext(requireContext()) .setLottieRawId(R.raw.route_animation).build() locationDialog = LottieDialog.Builder().setContext(requireContext()) .setLottieRawId(R.raw.taxi_animation).build() fragmentViewBinding.huaweiMap.apply { onCreate(null) getMapAsync(this@HomeFragment) setupBookingBottomSheet() } }

Java:

@Override public void setupUi() { super.setupUi(); lottieDialog = new LottieDialog(R.raw.route_animation, requireContext()); locationDialog = new LottieDialog(R.raw.taxi_animation, requireContext()); fragmentViewBinding.huaweiMap.onCreate(null); fragmentViewBinding.huaweiMap.getMapAsync(this); setupBookingBottomSheet(); }

Step 3: To add any marker on our map, we need to define a marker object with necessary and desired settings. To define the settings of the marker, use MarkerOptions().

Kotlin:

fun addTaxi(latLng: LatLng) { if (marker != null) { marker!!.remove() } val startLat = latLng.latitude - 0.001 val endLat = latLng.latitude + 0.001 val startLng = latLng.longitude - 0.001 val endLng = latLng.longitude + 0.001 for (i in 0..5) { marker = huaweiMap.addMarker( MarkerOptions().position( LatLng( Random.nextDouble(startLat, endLat), Random.nextDouble(startLng, endLng) ) ) .anchor(0.5f, 0.9f) .title("HiTaxi") .icon( BitmapDescriptorFactory.fromBitmap( ContextCompat.getDrawable( requireContext(), R.drawable.ic_taxi )?.toBitmap() ) ) ) } }

Java:

private void addTaxi(LatLng latLng) { if (marker != null) { marker.remove(); } double startLat = latLng.latitude - 0.001; double endLat = latLng.latitude + 0.001; double startLng = latLng.longitude - 0.001; double endLng = latLng.longitude + 0.001; for (int i = 0; i <= 5; i++) { double random = new Random().nextDouble(); double randomLat = startLat + (random * (endLat - startLat)); double randomLng = startLng + (random * (endLng - startLng)); Bitmap bitmap = BitmapFactory.decodeResource(requireContext().getResources(), R.drawable.ic_taxi); MarkerOptions markerOptions = new MarkerOptions().position(new LatLng(randomLat, randomLng)) .anchor(0.5f, 0.9f) .title("HiTaxi") .icon(BitmapDescriptorFactory.fromBitmap(bitmap)); marker = huaweiMap.addMarker( markerOptions); } }

Note: In this demo app, taxis are added as markers and randomly added close to user's location.

Step 4: To move the map programmatically, huaweiMap.moveCamera() method is used.

Kotlin:

private fun observeAndAddTaxi() { homeViewModel.lastKnownLocation.observe(viewLifecycleOwner) { it?.let { latLang -> if (homeViewModel.isDestinationSelectedBefore.value == false) { huaweiMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLang, 18f)) huaweiMap.clear() addTaxi(latLang) taxiFinalLocation = latLang } } } }

Java:

private void observeAndAddTaxi() { homeViewModel.getLastKnownLocation().observe(getViewLifecycleOwner(), latLng -> { if (homeViewModel.isDestinationSelectedBefore().getValue() == false) { huaweiMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 18f)); huaweiMap.clear(); addTaxi(latLng); taxiFinalLocation = latLng; } }); }

Step 1: Add below permissions to the AndroidManifest file as they are mandatory.

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

Step 2: Check for permissions.

Kotlin:

fun checkPermission(): Boolean { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (ActivityCompat.checkSelfPermission( context, android.Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission( context, android.Manifest.permission.ACCESS_COARSE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) { return false } } return true }

Java:

public boolean checkPermission(){ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return false; } } else { if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return false; } } return true; }

Step 3: Create a "FusedLocationProviderClient" instance to call location-related functions. Then try to get the last location of the user.

Kotlin:

fun getLastLocation(lastKnownLocation: (LatLng) -> Unit) { var mFusedLocationProviderClient: FusedLocationProviderClient? = null mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context) var latLng: LatLng try { val lastLocation: Task = mFusedLocationProviderClient!!.lastLocation lastLocation.addOnSuccessListener { if (it == null) { latLng = LatLng(0.0, 0.0) lastKnownLocation(latLng) } else { latLng = LatLng(it.latitude, it.longitude) lastKnownLocation(latLng) } }.addOnFailureListener { Log.e(TAG, "getLastLocation exception:" + it.message) } } catch (e: Exception) { Log.e(TAG, "getLastLocation exception:" + e.message) } }

Java:

public LatLng getLastLocation() { LatLng lastKnownLocation = new LatLng(0, 0); try { Task<Location> lastLocation = mFusedLocationProviderClient.getLastLocation(); lastLocation.addOnSuccessListener(new OnSuccessListener<Location>() { @Override public void onSuccess(Location location) { if (location == null) { lastKnownLocation.latitude = 0; lastKnownLocation.longitude = 0; } else { lastKnownLocation.latitude = location.getLatitude(); lastKnownLocation.longitude = location.getLongitude(); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { Log.e(TAG, "getLastLocation exception:" + e.getMessage()); } }); } catch (Exception e) { Log.e(TAG, "getLastLocation exception:" + e.getMessage()); } return lastKnownLocation; }

Step 4: The location function of the Location Kit depends on the device location settings. For example, if the location function is disabled on a device, your app cannot obtain the device location. Therefore, it is recommended that your app check whether the device settings meet the location requirements before continuously obtaining the device location. Location Kit provides the function of checking device location settings. After that, you can continuously obtain the device location using "requestLocationUpdates" function.

Kotlin:

fun checkLocationSettings(lastKnownLocation: (LatLng) -> Unit) { val settingsClient = LocationServices.getSettingsClient(context) val fusedLocationProviderClient: FusedLocationProviderClient val mLocationRequest: LocationRequest var latLng: LatLng fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context) val mLocationCallback: LocationCallback mLocationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult) { latLng = LatLng( locationResult.lastHWLocation.latitude, locationResult.lastHWLocation.longitude ) lastKnownLocation(latLng) } } val builder = LocationSettingsRequest.Builder() mLocationRequest = LocationRequest() builder.addLocationRequest(mLocationRequest) val locationSettingsRequest = builder.build() settingsClient.checkLocationSettings(locationSettingsRequest) .addOnSuccessListener { fusedLocationProviderClient .requestLocationUpdates( mLocationRequest, mLocationCallback, Looper.getMainLooper() ) } .addOnFailureListener { e -> val statusCode = (e as ApiException).statusCode when (statusCode) { LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try { val rae = e as ResolvableApiException rae.startResolutionForResult(context, 0) lastKnownLocation(LatLng(0.0, 0.0)) } catch (sie: IntentSender.SendIntentException) { lastKnownLocation(LatLng(0.0, 0.0)) } } } }

Java:

public void checkLocationSettings(){ LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder(); mLocationRequest = new LocationRequest(); builder.addLocationRequest(mLocationRequest); LocationSettingsRequest locationSettingsRequest = builder.build(); LatLng lastKnownLocation = new LatLng(0, 0); LocationCallback mLocationCallback; mLocationCallback = new LocationCallback() { @Override public void onLocationResult(LocationResult locationResult) { if (locationResult != null) { lastKnownLocation.latitude = locationResult.getLastLocation().getLatitude(); lastKnownLocation.longitude = locationResult.getLastLocation().getLongitude(); } } }; settingsClient.checkLocationSettings(locationSettingsRequest) .addOnSuccessListener(new OnSuccessListener<LocationSettingsResponse>() { @Override public void onSuccess(LocationSettingsResponse locationSettingsResponse) { mFusedLocationProviderClient .requestLocationUpdates( mLocationRequest, mLocationCallback, Looper.getMainLooper()); } }) .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(this, 0); } catch (IntentSender.SendIntentException sie) { } break; } } }); }

Step 5: To stop obtaining device location you can use "removeLocationUpdates" function.

Kotlin:

private fun stopLocationUpdates() { fusedLocationProviderClient.removeLocationUpdates(locationCallback) }

Java:

public void stopLocationUpdates() { mFusedLocationProviderClient.removeLocationUpdates(locationCallback); }

Upon completing the essential parts of the code, connect your mobile device to the PC and enable the USB debugging mode. In the Android Studio window, click icon to run the project you have created in Android Studio to generate an APK. Then install the APK on the mobile device.

Well done. You have successfully completed this codelab and learned:

For more information, please click the following links:

Site Kit

Map Kit

Location Kit

To download the sample code, please click the button below:
Download

Code copied