[Huawei Developers] HMS Community:
In this article, I will create a Doctor Consult Demo App along with the integration of Huawei Id, Map and Identity Kit. Which provides an easy interface to consult with doctor. Users can choose specific doctors and get the doctor details using Huawei User Address.

By reading this article, you'll get an overview of HMS Core Identity, Map and Account Kit, including its functions, open capabilities and business value.

HMS Core Map Service Introduction

HMS Core Map SDK is a set of APIs for map development in Android. The map data covers most countries outside China and supports multiple languages. The Map SDK uses the WGS 84 GPS coordinate system, which can meet most requirements of map development outside China. You can easily add map-related functions in your Android app, including:

Map display: Displays buildings, roads, water systems, and Points of Interest (POIs).

Map interaction: Controls the interaction gestures and buttons on the map.

Map drawing: Adds location markers, map layers, overlays, and various shapes.

Prerequisite

Huawei Phone EMUI 3.0 or later.

Non-Huawei phones Android 4.4 or later (API level 19 or higher).

Android Studio.

AppGallery Account.

App Gallery Integration process

Sign In and Create or Choose a project on AppGallery Connect portal.

Navigate to Project settings and download the configuration file.

Navigate to General Information, and then provide Data Storage location.

App Development

Create A New Project.

Configure Project Gradle.

Configure App Gradle.

apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'

android {
compileSdkVersion 30
buildToolsVersion "29.0.3"

defaultConfig {
applicationId "com.hms.doctorconsultdemo"
minSdkVersion 27
targetSdkVersion 30
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.cardview:cardview:1.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
//noinspection GradleCompatible
implementation 'com.android.support:recyclerview-v7:27.0.2'

implementation 'androidx.navigation:navigation-ui:2.1.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

implementation 'com.huawei.hms:identity:5.3.0.300'

//Dagger
implementation 'com.google.dagger:dagger:2.13'
annotationProcessor 'com.google.dagger:dagger-compiler:2.13'

//noinspection GradleCompatible
implementation 'com.android.support:cardview-v7:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.google.android.material:material:1.2.0'

implementation "com.google.code.gson:gson:2.8.5"
implementation('com.huawei.hms:hwid:4.0.4.300')
implementation "com.squareup.okhttp3:okhttp:3.14.2"
implementation 'com.squareup.okio:okio:1.14.1'

implementation 'com.github.bumptech.glide:glide:4.9.0'

implementation 'com.huawei.hms:ads-lite:13.4.30.307'
implementation 'com.huawei.hms:hianalytics:5.0.3.300'

//map
// implementation 'com.huawei.hms:maps:4.0.0.301'
implementation 'com.huawei.hms:maps:5.0.1.300'
//site
implementation 'com.huawei.hms:site:4.0.0.300'
//location
implementation 'com.huawei.hms:location:4.0.3.301'

implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation "com.squareup.retrofit2:adapter-rxjava2:2.6.2"

implementation 'io.reactivex:rxjava:1.3.0'
implementation 'io.reactivex:rxandroid:1.2.1'
implementation 'com.android.support:multidex:1.0.3'

implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2'

// RxAndroid
implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'

implementation 'com.huawei.hms:awareness:1.0.4.301'
implementation 'com.huawei.hms:dtm-api:5.0.2.300'
implementation 'com.huawei.agconnect:agconnect-remoteconfig:1.3.1.300'

implementation 'com.huawei.agconnect:agconnect-crash:1.4.1.300'
implementation "com.huawei.agconnect:agconnect-appmessaging:1.4.1.300"
implementation 'com.huawei.agconnect:agconnect-auth:1.4.1.300'

}

Configure AndroidManifest.xml.

Create Activity class with XML UI.

DirectionActivity:

package com.hms.doctorconsultdemo.map;

import android.Manifest;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.cardview.widget.CardView;
import androidx.core.app.ActivityCompat;
import androidx.lifecycle.ViewModelProviders;

import com.google.android.material.card.MaterialCardView;
import com.hms.doctorconsultdemo.BookAppointmentActivity;
import com.hms.doctorconsultdemo.R;
import com.hms.doctorconsultdemo.map.apiconnector.polylineBody.Destination;
import com.hms.doctorconsultdemo.map.apiconnector.polylineBody.Origin;
import com.hms.doctorconsultdemo.map.apiconnector.polylineBody.PolylineBody;
import com.hms.doctorconsultdemo.map.apiconnector.polylineResponse.Paths;
import com.hms.doctorconsultdemo.map.apiconnector.polylineResponse.Polyline;
import com.hms.doctorconsultdemo.map.apiconnector.polylineResponse.PolylineResponse;
import com.hms.doctorconsultdemo.map.apiconnector.polylineResponse.Routes;
import com.hms.doctorconsultdemo.map.apiconnector.polylineResponse.Steps;
import com.huawei.agconnect.remoteconfig.AGConnectConfig;
import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hms.common.ApiException;
import com.huawei.hms.common.ResolvableApiException;
import com.huawei.hms.location.FusedLocationProviderClient;
import com.huawei.hms.location.LocationAvailability;
import com.huawei.hms.location.LocationCallback;
import com.huawei.hms.location.LocationRequest;
import com.huawei.hms.location.LocationResult;
import com.huawei.hms.location.LocationServices;
import com.huawei.hms.location.LocationSettingsRequest;
import com.huawei.hms.location.LocationSettingsStatusCodes;
import com.huawei.hms.location.SettingsClient;
import com.huawei.hms.maps.CameraUpdateFactory;
import com.huawei.hms.maps.HuaweiMap;
import com.huawei.hms.maps.MapView;
import com.huawei.hms.maps.OnMapReadyCallback;
import com.huawei.hms.maps.SupportMapFragment;
import com.huawei.hms.maps.model.LatLng;
import com.huawei.hms.maps.model.MapStyleOptions;
import com.huawei.hms.maps.model.Marker;
import com.huawei.hms.maps.model.MarkerOptions;
import com.huawei.hms.maps.model.PolylineOptions;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DirectionActivity extends AppCompatActivity implements OnMapReadyCallback {

public static final String TAG = "DirectionActivity";
private static final String MAPVIEW_BUNDLE_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
private HuaweiMap hmap;
private MapView mMapView;
private Marker mMarker;
private List<LatLng> latLngList;

private MapApiViewModel mapApiViewModel;
private CardView cardView;

private LocationCallback mLocationCallback;
private LocationRequest mLocationRequest;
private FusedLocationProviderClient fusedLocationProviderClient;
private SettingsClient settingsClient;

private PolylineBody polylineBody;

private Button btnBooking;

private Map<String, Object> remoteConfigMap;
private AGConnectConfig config;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init();

remoteConfigMap = new HashMap<>();
config = AGConnectConfig.getInstance();
remoteConfigMap.put("mapstyle", "light");
config.applyDefault(remoteConfigMap);

SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView);

mMapView = findViewById(R.id.mapView);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY);
}
mMapView.onCreate(mapViewBundle);
mMapView.getMapAsync(DirectionActivity.this);
}

private void init() {
setContentView(R.layout.activity_direction);
cardView = findViewById(R.id.card_map);
btnBooking = findViewById(R.id.btn_book_trip);
btnBooking.setOnClickListener(view -> {
Intent intent = new Intent(this, BookAppointmentActivity.class);
startActivity(intent);
});
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);

Bundle extras = getIntent().getExtras();
if (extras != null) {
String name = extras.getString("name");
String orgLat = extras.getString("orgLat");
String orgLong = extras.getString("orgLong");
String desLat = extras.getString("desLat");
String desLong = extras.getString("desLong");
boolean tripDisplay = extras.getBoolean("isTrip");

if (!tripDisplay) {
cardView.setVisibility(View.GONE);
} else {
cardView.setVisibility(View.VISIBLE);
}

setTitle(name);
setLatLong(orgLat, orgLong, desLat, desLong);
}

mapApiViewModel = ViewModelProviders.of(this).get(MapApiViewModel.class);

fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
settingsClient = LocationServices.getSettingsClient(this);
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(10000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
if (null == mLocationCallback) {
mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult != null) {
List<Location> locations = locationResult.getLocations();
if (!locations.isEmpty()) {
for (Location location : locations) {
Log.i(TAG,
"onLocationResult location[Longitude,Latitude,Accuracy]:" + location.getLongitude()
+ "," + location.getLatitude() + "," + location.getAccuracy());
}
}
}
}

@Override
public void onLocationAvailability(LocationAvailability locationAvailability) {
if (locationAvailability != null) {
boolean flag = locationAvailability.isLocationAvailable();
Log.i(TAG, TAG + flag);
}
}
};
}

// check location permisiion
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 {
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 = {Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
"android.permission.ACCESS_BACKGROUND_LOCATION"};
ActivityCompat.requestPermissions(this, strings, 2);
}
}
}

@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 onMapReady(HuaweiMap map) {

hmap = map;

hmap.setMyLocationEnabled(true);
hmap.setTrafficEnabled(true);

hmap.getUiSettings().setRotateGesturesEnabled(true);
hmap.getUiSettings().setCompassEnabled(false);

mapApiViewModel.getPolylineLiveData(getPolylineBody()).observe(this, result -> {
Log.d(TAG, result.toString());
getPolylineData(result);
});

addHMSRemoteConfigListner();
}

private PolylineBody getPolylineBody() {
return polylineBody;
}

private void setLatLong(String orgLat, String orgLong, String desLat, String desLong) {
polylineBody = new PolylineBody();
Origin origin = new Origin();
origin.setLat(orgLat);
origin.setLng(orgLong);

Destination destination = new Destination();
destination.setLat(desLat);
destination.setLng(desLong);

polylineBody.setDestination(destination);
polylineBody.setOrigin(origin);
}

public void getPolylineData(PolylineResponse polylineResponse) {
List<Routes> routesList = polylineResponse.getRoutes();
List<Paths> paths = new ArrayList<>();
List<Steps> steps = new ArrayList<>();
List<Polyline> polylines = new ArrayList<>();
latLngList = new ArrayList<>();

for (int x = 0; x < routesList.size(); x++) {
for (Paths paths1 : routesList.get(x).getPaths()) {
paths.add(paths1);
}
for (int y = 0; y < paths.size(); y++) {
for (Steps step :
paths.get(y).getSteps()) {
steps.add(step);
}
}
for (int i = 0; i < steps.size(); i++) {
for (Polyline polyline :
steps.get(i).getPolyline()) {
polylines.add(polyline);
}
}
}

for (int i = 0; i < polylines.size(); i++) {
latLngList.add(new LatLng(Double.valueOf(polylines.get(i).getLat())
, Double.valueOf(polylines.get(i).getLng())));
}

hmap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLngList.get(0), 12.0f));
hmap.addMarker(new MarkerOptions().position(latLngList.get(0)));

hmap.addPolyline(new PolylineOptions()
.addAll(latLngList)
.color(Color.BLUE)
.width(3));

}

private void requestLocationUpdatesWithCallback() {
try {
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(mLocationRequest);
LocationSettingsRequest locationSettingsRequest = builder.build();
settingsClient.checkLocationSettings(locationSettingsRequest)
.addOnSuccessListener(locationSettingsResponse -> {
Log.i(TAG, "check location settings success");
fusedLocationProviderClient
.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.getMainLooper())
.addOnSuccessListener(aVoid -> Log.i(TAG, "requestLocationUpdatesWithCallback onSuccess"))
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.e(TAG,
"requestLocationUpdatesWithCallback onFailure:" + e.getMessage());
}
});
})
.addOnFailureListener(e -> {
Log.e(TAG, "checkLocationSetting onFailure:" + e.getMessage());
int statusCode = ((ApiException) e).getStatusCode();
switch (statusCode) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
try {
ResolvableApiException rae = (ResolvableApiException) e;
rae.startResolutionForResult(DirectionActivity.this, 0);
} catch (IntentSender.SendIntentException sie) {
Log.e(TAG, "PendingIntent unable to execute request.");
}
break;
}
});
} catch (Exception e) {
Log.e(TAG, "requestLocationUpdatesWithCallback exception:" + e.getMessage());
}
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
if (grantResults.length > 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "onRequestPermissionsResult: apply LOCATION PERMISSION successful");
} else {
Log.i(TAG, "onRequestPermissionsResult: apply LOCATION PERMISSSION failed");
}
}

if (requestCode == 2) {
if (grantResults.length > 2 && grantResults[2] == PackageManager.PERMISSION_GRANTED
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "onRequestPermissionsResult: apply ACCESS_BACKGROUND_LOCATION successful");
} else {
Log.i(TAG, "onRequestPermissionsResult: apply ACCESS_BACKGROUND_LOCATION failed");
}
}
}

private void addHMSRemoteConfigListner() {

config.fetch(5).addOnSuccessListener(configValues -> {
config.apply(configValues);
MapStyleOptions mapStyleOptions;
String style = config.getValueAsString("mapstyle");
String colorPrimary = config.getValueAsString("primarycolor");
Log.d(TAG, "HMS color : " + colorPrimary);

ActionBar actionBar = getSupportActionBar();
actionBar.setBackgroundDrawable(new ColorDrawable(Color.parseColor(colorPrimary)));

if (style.equalsIgnoreCase("dark")) {
mapStyleOptions = MapStyleOptions.loadRawResourceStyle(DirectionActivity.this, R.raw.mapstyle_night);
hmap.setMapStyle(mapStyleOptions);
} else if (style.equalsIgnoreCase("light")) {
mapStyleOptions = MapStyleOptions.loadRawResourceStyle(DirectionActivity.this, R.raw.mapstyle_day);
hmap.setMapStyle(mapStyleOptions);
}
}).addOnFailureListener((OnFailureListener) e -> {
Log.d(TAG, e.getMessage());
});
}
}

App Build Result

View attachment 5563697

Tips and Tricks

Map data of Map Kit does not cover the Chinese mainland. Therefore, the Map SDK for Android, Map SDK (Java) for HarmonyOS, JavaScript API, Static Map API, and Directions API of Map Kit are unavailable in the Chinese mainland. For details about locations where the services are available.

The map zoom icons flash on the map on devices running Android 8 or earlier before the map is loaded. (This issue occurs at a low probability in Android 8, but does not occur in versions later than Android 8.)

Layout file (XML file): Set uiZoomControls to false.

Code file: Set the parameter of the HuaweiMapOptions.zoomControlsEnabled(boolean isZoomControlsEnabled) method to false.

Conclusion

In this article, we have learned how to integrate HMS Core Identity and Map in Android application. After completely read this article user can easily implement Huawei User Address and Map APIs by HMS Core Identity, so that User can consult with doctor using Huawei User Address and redirect to Doctor Location.

Thanks for reading this article. Be sure to like and comment to this article, if you found it helpful. It means a lot to me.

References

HMS Identity Docs: https://developer.huawei.com/consumer/en/hms/huawei-identitykit/

https://developer.huawei.com/consum...des/android-sdk-introduction-0000001061991291

HMS Training Videos -

https://developer.huawei.com/consumer/en/training/