🗺️Search Places By Using The Huawei Map& Location Kits in Android | Kotlin🔍
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.
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
🪐 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.
🪐 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.
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.
💢 We can search the places, learn the coordinates and we can see the roadmap when we click on the destination marker.