🗺️Search Places By Using The Huawei Map& Location Kits in Android | Kotlin🔍

Merve Yönetci
Huawei Developers
Published in
7 min readNov 3, 2023
Search Place With HMS Kits

Introduction

Hello everyone!👋

In today’s article, I will talk about how we can search the places by using the Huawei Map Kit and Location Kit.

Source

Register to AppGallery Connect

First, you need to register to have a developer account by using this link. After you have an account, we can continue with the configuration.

Some Configurations on AppGallery Connect

🪐Click on the My Projects

🪐 Click on the Add project and fill in the blanks to add a project

🪐 Click on the Add app and add the information about the project that you are working on.

🪐 Click on the Manage APIs and enable Location and Map Kits

Click on the Manage APIs option
Enable location and map kits

🪐 After adding the app, we need to add a SHA-256 certificate fingerprint under the part of App Information on AG Connect. To learn the SHA-256 certificate fingerprint; click on the (1) Gradle and then click on the (2) Execute Gradle Task, write signingReport and enter. You will see what your SHA-256 fingerprint is.

(1) Gradle and (2) Execute Gradle Task

🪐 Download the agconnect-services.json file, switch Android Studio to the Project view and put the agconnect-services.json file in your app’s module directory.

Put json file under the app directory

Some Configurations on Android Studio

🪐 Add Maven addresses to the settings.gradle

pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
maven { url 'https://developer.huawei.com/repo/' }
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url 'https://developer.huawei.com/repo/' }
}
}
rootProject.name = "SearchPlaceWithMapKit"
include ':app'

🪐 Add dependencies to the gradle files;

Project Level:

buildscript {
dependencies {
classpath "com.android.tools.build:gradle:8.0.1"
classpath 'com.huawei.agconnect:agcp:1.9.1.300'
}
}
plugins {
id 'com.android.application' version '8.0.1' apply false
id 'com.android.library' version '8.0.1' apply false
id 'org.jetbrains.kotlin.android' version '1.8.20' apply false
}

App Level:

plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'com.huawei.agconnect'
}
//Fragment
implementation "androidx.fragment:fragment-ktx:1.6.1"

//Constraint Layout
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'

//AG Connect
implementation "com.huawei.agconnect:agconnect-core:1.9.1.300"

//Map Kit
implementation 'com.huawei.hms:maps:6.11.2.301'
implementation 'com.huawei.hms:maps-basic:6.11.2.301'

//Location Kit
implementation 'com.huawei.hms:location:6.12.0.300'

🪐 Add permissions on AndroidManifest.xml

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="com.huawei.appmarket.service.commondata.permission.GET_COMMON_DATA" />

Code Part

Let’s create an object named Constant and add the constants. You can see your project API key under the Project Information on AG Connect.

package com.example.searchplace.utils

object Constant {

const val MAPVIEW_BUNDLE_KEY = "MapViewBundleKey"
const val APP_GALLERY_PROJECT_API = "your_project_api"
}

Let’s create a fragment named MapFragment and let’s start with the fragment_map.xml file

We need an edit text to search for places, we need a button to get coordinates and we need a text view to see the latitude and longitude. Most importantly, we need a Huawei Map.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".map.MapFragment">

<Button
android:id="@+id/getCoordinate"
android:layout_width="183dp"
android:layout_height="48dp"
android:layout_marginTop="14dp"
android:layout_marginBottom="40dp"
android:text="Get Coordinate"
map:layout_constraintBottom_toTopOf="@+id/map"
map:layout_constraintEnd_toEndOf="parent"
map:layout_constraintHorizontal_bias="0.5"
map:layout_constraintStart_toStartOf="parent"
map:layout_constraintTop_toBottomOf="@+id/addressTextView" />

<com.huawei.hms.maps.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="500dp"
map:cameraTargetLat="48.893478"
map:cameraTargetLng="2.334595"
map:cameraZoom="10"
map:layout_constraintBottom_toBottomOf="parent"
map:layout_constraintEnd_toEndOf="parent"
map:layout_constraintStart_toStartOf="parent"
map:mapType="normal"
map:uiCompass="true"
map:uiZoomControls="true" />

<TextView
android:id="@+id/output"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="168dp"
android:layout_marginTop="17dp"
android:layout_marginEnd="186dp"
android:layout_marginBottom="16dp"
android:text="Lat: , Long: "
map:layout_constraintBottom_toTopOf="@+id/map"
map:layout_constraintEnd_toEndOf="parent"
map:layout_constraintHorizontal_bias="0.5"
map:layout_constraintStart_toStartOf="parent"
map:layout_constraintTop_toBottomOf="@+id/getCoordinate" />

<EditText
android:id="@+id/addressTextView"
android:layout_width="180dp"
android:layout_height="48dp"
android:ems="10"
android:hint="Search Address"
android:inputType="text"
android:visibility="visible"
map:layout_constraintEnd_toEndOf="parent"
map:layout_constraintHorizontal_bias="0.5"
map:layout_constraintStart_toStartOf="parent"
tools:layout_editor_absoluteY="15dp"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>

Now, we need to initialize the map in the MainActivity

class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

MapsInitializer.initialize(this)
MapsInitializer.setApiKey(Constant.APP_GALLERY_PROJECT_API)
}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainerView"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:defaultNavHost="true"
app:navGraph="@navigation/navigation"
tools:ignore="MissingConstraints"
tools:layout_editor_absoluteX="1dp"
tools:layout_editor_absoluteY="1dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

MapFragment

We will use geocoder to get latitude and longitude

package com.example.searchplace.map

import android.Manifest.permission.*
import android.content.*
import android.content.pm.PackageManager
import android.location.Geocoder
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.*
import androidx.annotation.RequiresPermission
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import com.example.searchplace.databinding.FragmentMapBinding
import com.example.searchplace.utils.Constant.MAPVIEW_BUNDLE_KEY
import com.google.android.material.snackbar.Snackbar
import com.huawei.hms.location.*
import com.huawei.hms.maps.*
import com.huawei.hms.maps.model.*
import java.io.IOException
import java.math.RoundingMode
import java.util.Locale


class MapFragment : Fragment(), OnMapReadyCallback {

private lateinit var binding: FragmentMapBinding
private var huaweiMap: HuaweiMap? = null
private var mMarker: Marker? = null
private lateinit var fusedLocationClient: FusedLocationProviderClient
private lateinit var geocoder: Geocoder


override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentMapBinding.inflate(inflater, container, false)

return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

val mMapView: MapView = binding.map
var mapViewBundle: Bundle? = null
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY)
}

mMapView.onCreate(mapViewBundle)
mMapView.getMapAsync(this)

fusedLocationClient = LocationServices.getFusedLocationProviderClient(requireContext())
geocoder = Geocoder(requireContext(), Locale.getDefault()) // initialise geocoder

// When clicking on the get coordinate button
binding.getCoordinate.setOnClickListener {
try {
val searchAddress =
geocoder.getFromLocationName(binding.addressTextView.text.toString(), 100)

if (searchAddress != null) {

val doubleLat = searchAddress[0]?.latitude
val doubleLong = searchAddress[0]?.longitude

if (doubleLat != null) {
if (doubleLong != null) {
binding.output.text = "Lat: " + doubleLat.toBigDecimal().setScale(
2,
RoundingMode.UP
) + ", " + "Long: " + doubleLong.toBigDecimal().setScale(
2,
RoundingMode.UP
)
}
}

val destinationLocation =
LatLng(searchAddress[0].latitude, searchAddress[0].longitude)
val cameraUpdate = CameraUpdateFactory.newLatLngZoom(destinationLocation, 15f)
huaweiMap?.moveCamera(cameraUpdate)

if (destinationLocation != null) {

val snackbar = Snackbar.make(
view, "Click on the marker if you want to get road map",
Snackbar.LENGTH_LONG
).setAction("Action", null)
snackbar.show()

// Add a marker the place you searched
val marker = huaweiMap?.addMarker(
MarkerOptions()
.position(destinationLocation)
.title("Address You Are Looking For")
.clusterable(true)
)
// Create a roadmap and go to petalmaps
marker?.tag =
"https://www.petalmaps.com/routes/?saddr=${mMarker?.position?.latitude},${mMarker?.position?.longitude}&daddr=${destinationLocation.latitude},${destinationLocation.longitude}&type=drive&utm_source=fb"

}
// Navigate to destination
huaweiMap?.setOnMarkerClickListener { marker ->
val navigationUri = marker.tag as? String
if (navigationUri != null) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(navigationUri))
if (intent.resolveActivity(requireContext().packageManager) != null) {
startActivity(intent)
} else {
Log.e(ContentValues.TAG, "No navigation app found")
}
}
true
}

}
} catch (e: IOException) {
e.printStackTrace()
}
}

// Get location permissions
if (ActivityCompat.checkSelfPermission(
requireContext(),
ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(
requireContext(),
ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {

val strings = arrayOf(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION)
ActivityCompat.requestPermissions(
requireActivity(),
strings,
1
)
} else {
getAndAddCurrentLocation()
}

}

@RequiresPermission(allOf = [ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE, ACCESS_COARSE_LOCATION])
override fun onMapReady(hMap: HuaweiMap?) {
if (hMap != null) {

huaweiMap = hMap

if (ActivityCompat.checkSelfPermission(
requireContext(),
ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(
requireContext(),
ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
//Enable your current location button
hMap.isMyLocationEnabled = true
hMap.uiSettings?.isMyLocationButtonEnabled = true

getAndAddCurrentLocation()
}
}
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)

var mapViewBundle: Bundle? = outState.getBundle(MAPVIEW_BUNDLE_KEY)
if (mapViewBundle == null) {
mapViewBundle = Bundle()
outState.putBundle(MAPVIEW_BUNDLE_KEY, mapViewBundle)
}
binding.map.onSaveInstanceState(mapViewBundle)
}

// Get your current location
private fun getAndAddCurrentLocation() {

fusedLocationClient.lastLocation
.addOnSuccessListener { location ->
if (location != null) {

val currentLocation =
geocoder.getFromLocation(location.latitude, location.longitude, 1)
val currentLatLng = LatLng(location.latitude, location.longitude)

Log.e("LOCATIONN", "$currentLatLng")

mMarker?.remove()

if (currentLocation != null) {
mMarker = huaweiMap?.addMarker(
MarkerOptions()
.position(currentLatLng)
.title("Your Location")
.snippet(currentLocation[0].getAddressLine(0))
)
}

huaweiMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(currentLatLng, 15f))

} else {
Log.e("LOCATIONULL", "Location is null")
}
}
.addOnFailureListener { e ->
Log.e(ContentValues.TAG, "Error getting location: ${e.message}")
}
}

override fun onStart() {
super.onStart()
binding.map.onStart()
}

override fun onResume() {
super.onResume()
binding.map.onResume()
}

override fun onPause() {
binding.map.onPause()
super.onPause()
}

override fun onStop() {
super.onStop()
binding.map.onStop()
}

override fun onDestroy() {
super.onDestroy()
binding.map.onDestroy()
}

override fun onLowMemory() {
super.onLowMemory()
binding.map.onLowMemory()
}
}

Conclusion

We can use Huawei Mobile Services if we want to search for the places on the map and create a roadmap. We created a developer account, integrated our app into AG Connect, implemented the map and location dependencies and wrote the necessary codes.

💢 We can see our current location, we can zoom out and zoom in on the map.

Zoom in on the map

💢 We can search the places, learn the coordinates and we can see the roadmap when we click on the destination marker.

Search the place and navigate to destination

--

--