Integration of Huawei Site Kit in KnowMyBoard using kotlin Part 4
Tags: android, kotlin, huawei, hms
Published: 2022-07-22 07:17:08

## Introduction

Site Kit is basically used for apps to provide the place related services. This kit provide to search the places with keyword, Find nearby place, place suggestion for user input, Get place details using the unique id.

**Features of Huawei Site Kit**

Keyword search: Returns a place list based on keywords entered by the user.   
Nearby place search: Searches for nearby places based on the current location of the user's device.
Place details: Searches for details about a place. 
Search suggestion: Returns a list of place suggestions.
Site Search: Returns a site object.
**Development Overview**

You need to install Android Studio IDE and I assume that you have prior knowledge of Android application development.

**Hardware Requirements**

A computer (desktop or laptop) running Windows 10.
Android phone (with the USB cable), which is used for debugging.
**Software Requirements**

Java JDK 1.8 or later.
Android Studio software or Visual Studio or Code installed.
HMS Core (APK) 4.X or later
**Integration steps**

Step 1. Huawei developer account and complete identity verification in Huawei developer website, refer to register Huawei ID.

Step 2. Create project in AppGallery Connect

Step 3. Adding HMS Core SDK

Let's start coding

MainActivity.kt


```
package com.huawei.hms.knowmyboard.dtse.activity.view

import androidx.navigation.Navigation.findNavController
import androidx.navigation.ui.NavigationUI.setupWithNavController
import com.huawei.hms.knowmyboard.dtse.activity.viewmodel.LoginViewModel
import android.graphics.Bitmap
import com.huawei.hms.mlsdk.langdetect.local.MLLocalLangDetector
import com.huawei.hms.mlsdk.translate.local.MLLocalTranslator
import android.app.ProgressDialog
import androidx.navigation.NavController
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import com.huawei.hms.knowmyboard.dtse.activity.app.MyApplication
import android.content.Intent
import com.huawei.hms.support.account.AccountAuthManager
import android.provider.MediaStore
import com.huawei.hmf.tasks.OnSuccessListener
import com.huawei.hms.mlsdk.langdetect.MLLangDetectorFactory
import com.huawei.hms.mlsdk.langdetect.local.MLLocalLangDetectorSetting
import com.huawei.hmf.tasks.OnFailureListener
import android.content.DialogInterface
import android.net.Uri
import android.util.Log
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.huawei.hms.common.ApiException
import com.huawei.hms.knowmyboard.dtse.R
import com.huawei.hms.knowmyboard.dtse.activity.model.UserData
import com.huawei.hms.knowmyboard.dtse.activity.util.Constants
import com.huawei.hms.knowmyboard.dtse.databinding.ActivityMainBinding
import com.huawei.hms.mlsdk.MLAnalyzerFactory
import com.huawei.hms.mlsdk.common.MLApplication
import com.huawei.hms.mlsdk.common.MLFrame
import com.huawei.hms.mlsdk.translate.local.MLLocalTranslateSetting
import com.huawei.hms.mlsdk.translate.MLTranslatorFactory
import com.huawei.hms.mlsdk.model.download.MLModelDownloadStrategy
import com.huawei.hms.mlsdk.model.download.MLModelDownloadListener
import com.huawei.hms.mlsdk.text.MLLocalTextSetting
import com.huawei.hms.mlsdk.text.MLText
import com.huawei.hms.mlsdk.text.MLTextAnalyzer
import java.io.IOException
import java.lang.Exception
import java.util.ArrayList

class MainActivity() : AppCompatActivity() {
    var loginViewModel: LoginViewModel? = null
    private var mTextAnalyzer: MLTextAnalyzer? = null
    var imagePath: Uri? = null
    var bitmap: Bitmap? = null
    var result = ArrayList<String>()
    var myLocalLangDetector: MLLocalLangDetector? = null
    var myLocalTranslator: MLLocalTranslator? = null
    var textRecognized: String? = null
    var progressDialog: ProgressDialog? = null
    var navController: NavController? = null
    var activityMainBinding: ActivityMainBinding? = null
    var bottomNavigationView: BottomNavigationView? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activityMainBinding =
            DataBindingUtil.setContentView(this@MainActivity, R.layout.activity_main)
        loginViewModel = ViewModelProvider(this@MainActivity).get(
            LoginViewModel::class.java
        )
        navController = findNavController(this@MainActivity, R.id.nav_host_fragment)
        MyApplication.activity = this
        progressDialog = ProgressDialog(this)
        progressDialog!!.setCancelable(false)
        bottomNavigationView = activityMainBinding!!.bottomNavigation
        setupWithNavController(bottomNavigationView!!, navController!!)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        // Process the authorization result to obtain the authorization code from AuthAccount.
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 1234) {
            Log.e("TAG", " Result can be pulled")
        }
        if (requestCode == 8888) {
            val authAccountTask = AccountAuthManager.parseAuthResultFromIntent(data)
            if (authAccountTask.isSuccessful) {
                // The sign-in is successful, and the user's ID information and authorization code are obtained.
                val authAccount = authAccountTask.result
                val userData = UserData()
                userData.accessToken = authAccount.accessToken
                userData.countryCode = authAccount.countryCode
                userData.displayName = authAccount.displayName
                userData.email = authAccount.email
                userData.familyName = authAccount.familyName
                userData.givenName = authAccount.givenName
                userData.idToken = authAccount.idToken
                userData.openId = authAccount.openId
                userData.uid = authAccount.uid
                userData.photoUriString = authAccount.avatarUri.toString()
                userData.unionId = authAccount.unionId
                loginViewModel = ViewModelProvider(this@MainActivity).get(
                    LoginViewModel::class.java
                )
                loginViewModel!!.sendData(authAccount.displayName)
            } else {
                // The sign-in failed.
                Log.e(
                    "TAG",
                    "sign in failed:" + (authAccountTask.exception as ApiException).statusCode
                )
            }
        }
        if ((requestCode == 2323) && (resultCode == RESULT_OK) && (data != null)) {
            progressDialog!!.setMessage("Initializing text detection..")
            progressDialog!!.show()
            imagePath = data.data
            try {
                bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, imagePath)
                asyncAnalyzeText(bitmap)
            } catch (e: IOException) {
                e.printStackTrace()
                Log.e("TAG", " BITMAP ERROR")
            }
        }
        if ((requestCode == 2424) && (resultCode == RESULT_OK) && (data != null)) {
            progressDialog!!.setMessage("Initializing text detection..")
            progressDialog!!.show()
            try {
                bitmap = data.extras!!["data"] as Bitmap?
                asyncAnalyzeText(bitmap)
            } catch (e: Exception) {
                e.printStackTrace()
                Log.e("TAG", " BITMAP ERROR")
            }
        }
    }

    private fun asyncAnalyzeText(bitmap: Bitmap?) {
        if (mTextAnalyzer == null) {
            createMLTextAnalyzer()
        }
        val frame = MLFrame.fromBitmap(bitmap)
        val task = mTextAnalyzer!!.asyncAnalyseFrame(frame)
        task.addOnSuccessListener(object : OnSuccessListener<MLText> {
            override fun onSuccess(text: MLText) {
                progressDialog!!.setMessage("Initializing language detection..")
                textRecognized = text.stringValue.trim { it <= ' ' }
                if (!textRecognized!!.isEmpty()) {
                    // Create a local language detector.
                    val factory = MLLangDetectorFactory.getInstance()
                    val setting =
                        MLLocalLangDetectorSetting.Factory() // Set the minimum confidence threshold for language detection.
                            .setTrustedThreshold(0.01f)
                            .create()
                    myLocalLangDetector = factory.getLocalLangDetector(setting)
                    val firstBestDetectTask = myLocalLangDetector!!.firstBestDetect(textRecognized)
                    firstBestDetectTask.addOnSuccessListener(OnSuccessListener { languageDetected ->
                        progressDialog!!.setMessage("Initializing text translation..")
                        // Processing logic for detection success.
                        textTranslate(languageDetected, textRecognized!!, bitmap)
                    }).addOnFailureListener(object : OnFailureListener {
                        override fun onFailure(e: Exception) {
                            // Processing logic for detection failure.
                            Log.e("TAG", "Lang detect error:" + e.message)
                        }
                    })
                } else {
                    progressDialog!!.dismiss()
                    showErrorDialog("Failed to recognize text.")
                }
            }
        }).addOnFailureListener(object : OnFailureListener {
            override fun onFailure(e: Exception) {
                Log.e("TAG", "#==>" + e.message)
            }
        })
    }

    private fun showErrorDialog(msg: String) {
        val alertDialog = AlertDialog.Builder(this).create()
        alertDialog.setTitle("Error")
        alertDialog.setMessage(msg)
        alertDialog.setButton(
            AlertDialog.BUTTON_POSITIVE,
            "OK",
            object : DialogInterface.OnClickListener {
                override fun onClick(dialog: DialogInterface, which: Int) {
                    dialog.dismiss()
                }
            })
        alertDialog.show()
    }

    private fun textTranslate(languageDetected: String, textRecognized: String, uri: Bitmap?) {
        MLApplication.initialize(application)
        MLApplication.getInstance().apiKey = Constants.API_KEY

        // Create an offline translator.
        val setting =
            MLLocalTranslateSetting.Factory() // Set the source language code. The ISO 639-1 standard is used. This parameter is mandatory. If this parameter is not set, an error may occur.
                .setSourceLangCode(languageDetected) // Set the target language code. The ISO 639-1 standard is used. This parameter is mandatory. If this parameter is not set, an error may occur.
                .setTargetLangCode("en")
                .create()
        myLocalTranslator = MLTranslatorFactory.getInstance().getLocalTranslator(setting)
        // Set the model download policy.
        val downloadStrategy = MLModelDownloadStrategy.Factory()
            .needWifi() // It is recommended that you download the package in a Wi-Fi environment.
            .create()
        // Create a download progress listener.
        val modelDownloadListener: MLModelDownloadListener = object : MLModelDownloadListener {
            override fun onProcess(alreadyDownLength: Long, totalLength: Long) {
                runOnUiThread(object : Runnable {
                    override fun run() {
                        // Display the download progress or perform other operations.
                    }
                })
            }
        }
        myLocalTranslator!!.preparedModel(downloadStrategy, modelDownloadListener)
            .addOnSuccessListener(object : OnSuccessListener<Void?> {
                override fun onSuccess(aVoid: Void?) {
                    // Called when the model package is successfully downloaded.
                    // input is a string of less than 5000 characters.
                    val task = myLocalTranslator!!.asyncTranslate(textRecognized)
                    // Before translation, ensure that the models have been successfully downloaded.
                    task.addOnSuccessListener(object : OnSuccessListener<String> {
                        override fun onSuccess(translated: String) {
                            // Processing logic for detection success.
                            result.clear()
                            result.add(languageDetected.trim { it <= ' ' })
                            result.add(textRecognized.trim { it <= ' ' })
                            result.add(translated.trim { it <= ' ' })
                            loginViewModel!!.setImage(uri!!)
                            loginViewModel!!.setTextRecongnized(result)
                            progressDialog!!.dismiss()
                        }
                    }).addOnFailureListener(object : OnFailureListener {
                        override fun onFailure(e: Exception) {
                            // Processing logic for detection failure.
                            progressDialog!!.dismiss()
                        }
                    })
                }
            }).addOnFailureListener(object : OnFailureListener {
            override fun onFailure(e: Exception) {
                // Called when the model package fails to be downloaded.
                progressDialog!!.dismiss()
            }
        })
    }

    private fun createMLTextAnalyzer() {
        val setting = MLLocalTextSetting.Factory()
            .setOCRMode(MLLocalTextSetting.OCR_DETECT_MODE)
            .create()
        mTextAnalyzer = MLAnalyzerFactory.getInstance().getLocalTextAnalyzer(setting)
    }

    override fun onStop() {
        if (myLocalLangDetector != null) {
            myLocalLangDetector!!.stop()
        }
        if (myLocalTranslator != null) {
            myLocalTranslator!!.stop()
        }
        if (progressDialog != null) {
            progressDialog!!.dismiss()
        }
        super.onStop()
    }

    companion object {
        var TAG = "TAG"
    }
}
```

LoginViewModel.kt


```
package com.huawei.hms.knowmyboard.dtse.activity.viewmodel

import android.app.Application
import com.huawei.hms.support.account.service.AccountAuthService
import android.graphics.Bitmap
import com.huawei.hms.location.LocationResult
import com.huawei.hms.site.api.model.Site
import android.widget.Toast
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.huawei.hms.common.ApiException
import com.huawei.hms.knowmyboard.dtse.R
import com.huawei.hms.support.account.request.AccountAuthParams
import com.huawei.hms.support.account.request.AccountAuthParamsHelper
import com.huawei.hms.support.account.AccountAuthManager
import com.huawei.hms.knowmyboard.dtse.activity.app.MyApplication
import java.util.ArrayList

class LoginViewModel(application: Application) : AndroidViewModel(application) {
    var service: AccountAuthService? = null
    val message = MutableLiveData<String>()
    val textRecongnized = MutableLiveData<ArrayList<String>>()
    val imagePath = MutableLiveData<Bitmap>()
    val locationResult = MutableLiveData<LocationResult>()
    val siteSelected = MutableLiveData<Site>()
    fun getSiteSelected(): LiveData<Site> {
        return siteSelected
    }

    fun setSiteSelected(siteSelected: Site) {
        this.siteSelected.value = siteSelected
    }

    fun sendData(msg: String) {
        message.value = msg
    }

    fun getMessage(): LiveData<String> {
        return message
    }

    fun setImage(imagePath: Bitmap) {
        this.imagePath.value = imagePath
    }

    fun setLocationResult(locationResult: LocationResult) {
        this.locationResult.value = locationResult
    }

    fun setTextRecongnized(textRecongnized: ArrayList<String>) {
        this.textRecongnized.value = textRecongnized
    }

    fun logoutHuaweiID() {
        if (service != null) {
            service!!.signOut()
            sendData("KnowMyBoard")
            Toast.makeText(getApplication(), "You are logged out from Huawei ID", Toast.LENGTH_LONG)
                .show()
        }
    }

    fun loginClicked() {
        val authParams =
            AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM).setAuthorizationCode()
                .createParams()
        service = AccountAuthManager.getService(MyApplication.activity, authParams)
        MyApplication.activity!!.startActivityForResult(service!!.signInIntent, 8888)
    }

    fun cancelAuthorization() {
        if (service != null) {
            // service indicates the AccountAuthService instance generated using the getService method during the sign-in authorization.
            service!!.cancelAuthorization().addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    // Processing after a successful authorization cancellation.
                    sendData(getApplication<Application>().resources.getResourceName(R.string.app_name))
                    Toast.makeText(getApplication(), "Cancelled authorization", Toast.LENGTH_LONG)
                        .show()
                } else {
                    // Handle the exception.
                    val exception = task.exception
                    if (exception is ApiException) {
                        val statusCode = exception.statusCode
                        Toast.makeText(
                            getApplication(),
                            "Failed to cancel authorization. status code $statusCode",
                            Toast.LENGTH_LONG
                        ).show()
                    }
                }
            }
        } else {
            Toast.makeText(getApplication(), "Login required", Toast.LENGTH_LONG).show()
        }
    }
}
```

SearchFragment.kt


```
package com.huawei.hms.knowmyboard.dtse.activity.fragments

import androidx.navigation.Navigation.findNavController
import com.huawei.hms.knowmyboard.dtse.activity.viewmodel.LoginViewModel
import androidx.navigation.NavController
import com.huawei.hms.site.api.SearchService
import com.huawei.hms.knowmyboard.dtse.activity.adapter.SitesAdapter
import com.huawei.hms.site.api.model.Site
import com.huawei.hms.location.LocationResult
import com.huawei.hms.knowmyboard.dtse.activity.intefaces.ItemClickListener
import android.view.WindowManager
import android.view.LayoutInflater
import android.view.ViewGroup
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.appcompat.widget.SearchView
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.huawei.hms.knowmyboard.dtse.R
import com.huawei.hms.knowmyboard.dtse.activity.util.Constants
import com.huawei.hms.knowmyboard.dtse.databinding.FragmentSearchBinding
import com.huawei.hms.site.api.SearchServiceFactory
import com.huawei.hms.site.api.model.TextSearchRequest
import com.huawei.hms.site.api.model.Coordinate
import com.huawei.hms.site.api.SearchResultListener
import com.huawei.hms.site.api.model.TextSearchResponse
import com.huawei.hms.site.api.model.SearchStatus
import com.huawei.hms.site.api.model.NearbySearchRequest
import com.huawei.hms.site.api.model.HwLocationType
import com.huawei.hms.site.api.model.NearbySearchResponse
import java.io.UnsupportedEncodingException
import java.lang.Exception
import java.net.URLEncoder
import java.util.ArrayList

class SearchFragment : Fragment() {
    var binding: FragmentSearchBinding? = null
    var loginViewModel: LoginViewModel? = null

    //View view;
    var navController: NavController? = null
    private var searchService: SearchService? = null
    var adapter: SitesAdapter? = null
    var siteArrayList = ArrayList<Site>()
    var locationResult: LocationResult? = null
   /* var siteClicklistener = ItemClickListener { vh, site, pos ->
        requireActivity().window.setSoftInputMode(
            WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
        )
        loginViewModel!!.setSiteSelected(site)
        navController!!.navigate(R.id.loginFragment)
    }*/

    var siteClicklistener = object : ItemClickListener{
        override fun onItemClicked(vh: RecyclerView.ViewHolder?, item: Site?, pos: Int) {
            requireActivity().window.setSoftInputMode(
                WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
            )
            loginViewModel!!.setSiteSelected(item!!)
            navController!!.navigate(R.id.loginFragment)
        }

    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        requireActivity().window.setSoftInputMode(
            WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
        )
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_search, container, false)
        loginViewModel = ViewModelProvider(requireActivity()).get(
            LoginViewModel::class.java
        )
        val searchView = binding!!.edSearch
        val recyclerView = binding!!.suggestionRv
        navController = findNavController(requireActivity(), R.id.nav_host_fragment)
        searchView.isFocusable = true
        searchView.onActionViewExpanded()
        adapter = SitesAdapter(siteArrayList, context, siteClicklistener)
        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(context)
        recyclerView.setHasFixedSize(true)
        loginViewModel!!.locationResult.observeForever { locationResult1 ->
            locationResult = locationResult1
        }
        searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
            override fun onQueryTextSubmit(query: String): Boolean {
                return false
            }

            override fun onQueryTextChange(newText: String): Boolean {
                if (newText.length > 4) {
                    nearByPlacesSearch(newText)
                }
                return false
            }
        })
        return binding!!.root
    }

    fun keywordSearch(search: String?) {
        try {
            val key = URLEncoder.encode(Constants.API_KEY, "UTF-8")
            // Instantiate the SearchService object.
            searchService = SearchServiceFactory.create(context, key)
            // Create a request body.
            val request = TextSearchRequest()
            request.query = search
            if (locationResult != null) {
                val location = Coordinate(
                    locationResult!!.lastHWLocation.latitude,
                    locationResult!!.lastHWLocation.longitude
                )
                request.location = location
            }
            request.radius = 1000
            //request.setHwPoiType(HwLocationType.HOTEL_MOTEL);
            request.countryCode = "IN"
            request.language = "en"
            request.pageIndex = 1
            request.pageSize = 5
            request.isChildren = false
            // request.setCountries(Arrays.asList("en", "fr", "cn", "de", "ko","in"));
            // Create a search result listener.
            val resultListener: SearchResultListener<TextSearchResponse?> =
                object : SearchResultListener<TextSearchResponse?> {
                    // Return search results upon a successful search.
                    override fun onSearchResult(results: TextSearchResponse?) {
                        if (results == null || results.totalCount <= 0) {
                            return
                        }
                        val sites = results.sites
                        if (sites == null || sites.size == 0) {
                            return
                        }
                        siteArrayList.clear()
                        for (site in sites) {
                            siteArrayList.add(site)
                        }
                        siteArrayList.addAll(sites)
                        adapter!!.notifyDataSetChanged()
                    }

                    // Return the result code and description upon a search exception.
                    override fun onSearchError(status: SearchStatus) {
                        Log.i("TAG", "Error : " + status.errorCode + " " + status.errorMessage)
                    }
                }
            // Call the keyword search API.
            searchService!!.textSearch(request, resultListener)
        } catch (e: UnsupportedEncodingException) {
            e.printStackTrace()
        }
    }

    fun nearByPlacesSearch(newText: String?) {
        try {
            val key = URLEncoder.encode(Constants.API_KEY, "UTF-8")
            // Instantiate the SearchService object.
            searchService = SearchServiceFactory.create(context, key)
            // Create a request body.
            val request = NearbySearchRequest()
            if (locationResult != null) {
                val location = Coordinate(
                    locationResult!!.lastHWLocation.latitude,
                    locationResult!!.lastHWLocation.longitude
                )
                request.location = location
            }
            request.query = newText
            request.radius = 1000
            request.hwPoiType = HwLocationType.ADDRESS
            request.language = "en"
            request.pageIndex = 1
            request.pageSize = 5
            request.strictBounds = false
            // Create a search result listener.
            val resultListener: SearchResultListener<NearbySearchResponse?> =
                object : SearchResultListener<NearbySearchResponse?> {
                    // Return search results upon a successful search.
                    override fun onSearchResult(results: NearbySearchResponse?) {
                        if (results == null || results.totalCount <= 0) {
                            return
                        }
                        val sites = results.sites
                        if (sites == null || sites.size == 0) {
                            return
                        }
                        siteArrayList.clear()
                        for (site in sites) {
                            siteArrayList.add(site)
                        }
                        siteArrayList.addAll(sites)
                        adapter!!.notifyDataSetChanged()
                    }

                    // Return the result code and description upon a search exception.
                    override fun onSearchError(status: SearchStatus) {
                        Log.i("TAG", "Error : " + status.errorCode + " " + status.errorMessage)
                    }
                }
            // Call the nearby place search API.
            searchService!!.nearbySearch(request, resultListener)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}
```

RequestLocationData.kt


```
package com.huawei.hms.knowmyboard.dtse.activity.util

import android.Manifest
import com.huawei.hms.knowmyboard.dtse.activity.viewmodel.LoginViewModel
import android.app.Activity
import android.content.Context
import android.os.Build
import android.content.pm.PackageManager
import android.os.Looper
import android.location.Geocoder
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.app.ActivityCompat
import androidx.fragment.app.FragmentActivity
import com.huawei.hms.location.*
import java.io.IOException
import java.lang.StringBuilder
import java.util.*

class RequestLocationData(
    context: Context?,
    activity: FragmentActivity?,
    loginViewModel: LoginViewModel?
) {
    private var settingsClient: SettingsClient? = null
    private var isLocationSettingSuccess = 0
    private var myLocationRequest: LocationRequest? = null

    // Define a fusedLocationProviderClient object.
    private var fusedLocationProviderClient: FusedLocationProviderClient? = null
    private var myLocationCallback: LocationCallback? = null
    var context: Context? = null
    var activity: Activity? = null
    private var locationResult: LocationResult? = null
    var loginViewModel: LoginViewModel? = null
    fun initFusionLocationProviderClint() {
        // Instantiate the fusedLocationProviderClient object.
        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(activity)
        this.settingsClient = LocationServices.getSettingsClient(activity)
    }

    fun checkDeviceLocationSettings() {
        val builder = LocationSettingsRequest.Builder()
        myLocationRequest = LocationRequest()
        builder.addLocationRequest(myLocationRequest)
        val locationSettingsRequest = builder.build()
        // Check the device location settings.
        settingsClient!!.checkLocationSettings(locationSettingsRequest) // Define the listener for success in calling the API for checking device location settings.
            .addOnSuccessListener { locationSettingsResponse: LocationSettingsResponse ->
                val locationSettingsStates = locationSettingsResponse.locationSettingsStates
                val stringBuilder = StringBuilder()
                // Check whether the location function is enabled.
                stringBuilder.append(",\nisLocationUsable=")
                    .append(locationSettingsStates.isLocationUsable)
                // Check whether HMS Core (APK) is available.
                stringBuilder.append(",\nisHMSLocationUsable=")
                    .append(locationSettingsStates.isHMSLocationUsable)
                Log.i(TAG, "checkLocationSetting onComplete:$stringBuilder")
                // Set the location type.
                myLocationRequest!!.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
                // Set the number of location updates to 1.
                myLocationRequest!!.numUpdates = 1
                isLocationSettingSuccess = 1
            } // Define callback for failure in checking the device location settings.
            .addOnFailureListener { e -> Log.i(TAG, "checkLocationSetting onFailure:" + e.message) }
    }

    @RequiresApi(Build.VERSION_CODES.S)
    fun checkPermission() {
        // Dynamically apply for required permissions if the API level is 28 or lower.
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
            Log.i(TAG, "android sdk <= 28 Q")
            if (ActivityCompat.checkSelfPermission(
                    context!!,
                    Manifest.permission.ACCESS_FINE_LOCATION
                ) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(
                    context!!,
                    Manifest.permission.ACCESS_COARSE_LOCATION
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                val strings = arrayOf(
                    Manifest.permission.CAMERA,
                    Manifest.permission.MANAGE_MEDIA,
                    Manifest.permission.MEDIA_CONTENT_CONTROL,
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION
                )
                ActivityCompat.requestPermissions(activity!!, strings, 1)
            }
        } else {
            // Dynamically apply for the android.permission.ACCESS_BACKGROUND_LOCATION permission in addition to the preceding permissions if the API level is higher than 28.
            if (ActivityCompat.checkSelfPermission(
                    activity!!,
                    Manifest.permission.ACCESS_FINE_LOCATION
                ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
                    context!!,
                    Manifest.permission.ACCESS_COARSE_LOCATION
                ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
                    context!!,
                    "android.permission.ACCESS_BACKGROUND_LOCATION"
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                val strings = arrayOf(
                    Manifest.permission.CAMERA,
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION,
                    Manifest.permission.MEDIA_CONTENT_CONTROL,
                    Manifest.permission.MANAGE_MEDIA,
                    "android.permission.ACCESS_BACKGROUND_LOCATION"
                )
                ActivityCompat.requestPermissions(activity!!, strings, 2)
            }
        }
    }

    fun refreshLocation(): LocationResult? {
        Log.d(TAG, "Refreshing location")
        if (isLocationSettingSuccess == 1) {
            myLocationCallback = object : LocationCallback() {
                private var locationResult: LocationResult? = null
                override fun onLocationResult(locationResult: LocationResult) {
                    if (locationResult != null) {
                        // Gson gson = new Gson();
                        //Log.d(TAG, " Location data :" + locationResult.getLastLocation().getLatitude() + " : " + locationResult.getLastLocation().getLongitude());
                        //Log.d(TAG, " Location data :" + gson.toJson(locationResult.getLastHWLocation()));
                        //Log.d(TAG, " Location data :" + locationResult.getLastHWLocation().getCountryName());
                        Log.d(TAG, " Location data :" + locationResult.lastHWLocation.latitude)
                        Log.d(TAG, " Location data :" + locationResult.lastHWLocation.longitude)
                        // binding.textDetected.setText("Latitude " + locationResult.getLastHWLocation().getLatitude() + " Longitude " + locationResult.getLastHWLocation().getLongitude());
                        //getGeoCoderValues(locationResult.getLastHWLocation().getLatitude(),locationResult.getLastHWLocation().getLongitude());
                        this.locationResult = locationResult
                        loginViewModel!!.setLocationResult(locationResult)
                    }
                }
            }
            fusedLocationProviderClient!!.requestLocationUpdates(
                myLocationRequest,
                myLocationCallback,
                Looper.getMainLooper()
            )
        } else {
            Log.d(TAG, "Failed to get location settings")
        }
        return locationResult
    }

    fun disableLocationData() {
        fusedLocationProviderClient!!.disableBackgroundLocation()
        fusedLocationProviderClient!!.removeLocationUpdates(myLocationCallback)
    }

    private fun getGeoCoderValues(latitude: Double, longitude: Double) {
        getAddress(context, latitude, longitude)
        /*  Geocoder geocoder;
        List<Address> addresses;
        Locale locale = new Locale("en", "IN");
        geocoder = new Geocoder(getContext(), locale);

        try {
            addresses = geocoder.getFromLocation(latitude, longitude, 1); // Here 1 represent max location result to returned, by documents it recommended 1 to 5
            Gson gson=new Gson();
            Log.d(TAG,"Geo coder :"+gson.toJson(addresses));
        String address = addresses.get(0).getAddressLine(0); // If any additional address line present than only, check with max available address lines by getMaxAddressLineIndex()
        String city = addresses.get(0).getLocality();
        String state = addresses.get(0).getAdminArea();
        String country = addresses.get(0).getCountryName();
        String postalCode = addresses.get(0).getPostalCode();
        String knownName = addresses.get(0).getFeatureName();
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(TAG,"Error while fetching Geo coder :"+e.getMessage());
        }*/
        /* Locale locale = new Locale("en", "IN");
        GeocoderService geocoderService =
                LocationServices.getGeocoderService(getActivity().getBaseContext(), locale);
        // Request reverse geocoding.
        GetFromLocationRequest getFromLocationRequest = new GetFromLocationRequest(latitude, longitude, 5);
        // Initiate reverse geocoding.
        geocoderService.getFromLocation(getFromLocationRequest)
                .addOnSuccessListener(hwLocation -> {
                    Gson gson=new Gson();
                    Log.d(TAG,"Geo coder :"+gson.toJson(hwLocation));

                })
                .addOnFailureListener(e -> {

                    Log.e(TAG,"Error while fetching Geo coder :"+e.getMessage());
                });*/
    }

    companion object {
        var TAG = "TAG"
        fun getAddress(context: Context?, LATITUDE: Double, LONGITUDE: Double) {
            //Set Address
            try {
                val geocoder = Geocoder(context, Locale.getDefault())
                val addresses = geocoder.getFromLocation(LATITUDE, LONGITUDE, 1)
                if (addresses != null && addresses.size > 0) {
                    val address =
                        addresses[0].getAddressLine(0) // If any additional address line present than only, check with max available address lines by getMaxAddressLineIndex()
                    val city = addresses[0].locality
                    val state = addresses[0].adminArea
                    val country = addresses[0].countryName
                    val postalCode = addresses[0].postalCode
                    val knownName = addresses[0].featureName // Only if available else return NULL
                    Log.d(TAG, "getAddress:  address$address")
                    Log.d(TAG, "getAddress:  city$city")
                    Log.d(TAG, "getAddress:  state$state")
                    Log.d(TAG, "getAddress:  postalCode$postalCode")
                    Log.d(TAG, "getAddress:  knownName$knownName")
                }
            } catch (e: IOException) {
                e.printStackTrace()
                Log.e(TAG, "Error while fetching Geo coder :" + e.message)
            }
        }
    }

    init {
        this@RequestLocationData.context = context
        this@RequestLocationData.activity = activity
        this@RequestLocationData.loginViewModel = loginViewModel
    }
}
```

navigation_graph.xml

```
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/navigation_graph"
    app:startDestination="@id/loginFragment">
    <fragment
        android:id="@+id/loginFragment"
        android:name="com.huawei.hms.knowmyboard.dtse.activity.fragments.LoginFragment"
        android:label="LoginFragment"/>
    <fragment
        android:id="@+id/mainFragment"
        android:name="com.huawei.hms.knowmyboard.dtse.activity.fragments.MainFragment"
        android:label="MainFragment"/>
    <fragment
        android:id="@+id/searchFragment"
        android:name="com.huawei.hms.knowmyboard.dtse.activity.fragments.SearchFragment"
        android:label="fragment_search"
        tools:layout="@layout/fragment_search" />
</navigation>
```
bottom_navigation_menu.xml

```
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/searchFragment"
        android:icon="@android:drawable/ic_menu_search"
        android:title="@string/search" />
    <item
        android:id="@+id/loginFragment"
        android:icon="@android:drawable/ic_menu_agenda"
        android:title="Home" />
    <item
        android:id="@+id/mainFragment"
        app:showAsAction="always"
        android:icon="@android:drawable/ic_menu_gallery"
        android:title="Gallery" />
</menu>
```

## Result
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i9kednjbdfrhxdw0vbow.png)
 
## Tricks and Tips

Makes sure that agconnect-services.json file added.
Make sure required dependencies are added
Make sure that service is enabled in AGC
Enable data binding in gradle.build file
Make sure bottom navigation id's should be same as fragment id's in navigation graph
## Conclusion

In this article, we have learnt how to integrate Huawei Site Kit, Map kit, Location kit in Android application KnowMyBoard. Hoping that the Map kit capabilities are helpful to you as well, like this sample, you can make use of Site kit as per your requirement.

## Reference

[Map Kit](https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-sdk-introduction-0000001061991291) – [Training video](https://developer.huawei.com/consumer/en/training/course/video/101590716045317017)

[Site Kit](https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-sdk-introduction-0000001050158571) – [Training video](https://developer.huawei.com/consumer/en/training/course/video/101582996919057158)