Expert: Doctor Consult using RxAndroid and MVVM with Huawei Kits (Account, Map, Identity) in Android App.
Published: 2022-03-11 12:06:56

## 
Overview
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
1.Huawei Phone EMUI 3.0 or later.
2.Non-Huawei phones Android 4.4 or later (API level 19 or higher).
3.Android Studio.
4.AppGallery Account.

## 
App Gallery Integration process
1.Sign In and Create or Choose a project on AppGallery Connect portal.
2.Navigate to Project settings and download the configuration file.
3.Navigate to General Information, and then provide Data Storage location.

## 
App Development
1.Create A New Project.
2.Configure Project Gradle.

```
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        google()
        jcenter()
        maven {url 'https://developer.huawei.com/repo/'}
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.0.1"
        classpath 'com.huawei.agconnect:agcp:1.4.2.300'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        maven {url 'https://developer.huawei.com/repo/'}
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
```
3.Configure App Gradle.

```
//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'

```
4.Configure AndroidManifest.xml.

```
 <meta-data
            android:name="install_channel"
            android:value="AppGallery" />
        <meta-data
            android:name="com.huawei.hms.client.appid"
            android:value="XXXXXXX" />
```
5.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


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x0ty8ij0i7p73hgw7k1t.png)

## 
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/consumer/en/doc/development/HMSCore-Guides/android-sdk-introduction-0000001061991291
HMS Training Videos - 
https://developer.huawei.com/consumer/en/training/