전문가: Kotlin을 사용하는 Android의 택배 앱 MVVM Jetpack(HMS 위치 및 지도 키트) - Part-5

38040 단어
개요

이 기사에서는 HMS Account, Push, CloudDB, AuthService, Push Kit Uplink Message, Location 및 Map Kit와 같은 HMS Core 키트를 통합할 Kotlin을 사용하여 Courier Android 애플리케이션을 만들 것입니다.

HMS Account 및 AuthService 키트를 부분적으로 통합했으며, HMS 푸시 키트를 사용한 푸시 알림(2부), 클라우드 DB 키트(3부) 및 화웨이 클라이언트 푸시 통합을 이 시리즈의 4부로 통합했습니다. 아래 링크로 들어가주세요-

파트 1 https://forums.developer.huawei.com/forumPortal/en/topic/0202841957497640128
파트 2 https://forums.developer.huawei.com/forumPortal/en/topic/0201847982965230092
3부 https://forums.developer.huawei.com/forumPortal/en/topic/0201854022878900124?fid=0101187876626530001

앱은 DataBinding, AndroidViewModel, Observer, LiveData 등과 같은 Jetpack 구성 요소를 사용하여 Android MVVM 클린 아키텍처를 사용합니다.

이 기사에서는 Observable 패턴을 사용하여 DataBinding을 구현하려고 합니다.

지도 키트 소개

Map Kit는 200개 이상의 국가 및 지역의 지도 데이터를 포함하고 70개 이상의 언어를 지원합니다. 사용자는 SDK를 사용하여 지도 기반 기능을 앱에 쉽게 통합할 수 있습니다. 지도 세부 정보 표시 기능을 최적화하고 강화합니다. Map Kit는 줌, 회전, 이동 및 기울기 제스처를 포함한 제스처를 지원하여 원활한 상호작용 경험을 보장합니다.

로케이션 키트 소개

Location Kit는 GPS, Wi-Fi 및 기지국 위치 기능을 앱에 결합하여 글로벌 포지셔닝 기능을 구축하고 전 세계 사용자를 대상으로 유연한 위치 기반 서비스를 제공할 수 있습니다. 현재 통합 위치, 활동 식별 및 지오펜스의 세 가지 주요 기능을 제공합니다. 필요에 따라 이러한 기능 중 하나 이상을 호출할 수 있습니다.

전제 조건

Huawei Phone EMUI 3.0 이상.
Huawei가 아닌 휴대폰 Android 4.4 이상(API 레벨 19 이상).
HMS 코어 APK 4.0.0.300 이상
안드로이드 스튜디오
AppGallery 계정
앱 갤러리 통합 프로세스

로그인하고 AppGallery Connect 포털에서 프로젝트를 생성하거나 선택합니다.

프로젝트 설정으로 이동하여 구성 파일을 다운로드합니다.

일반 정보로 이동한 다음 데이터 저장소 위치를 제공합니다.

앱 개발

필수 종속성 추가:
Android 스튜디오를 시작하고 새 프로젝트를 만듭니다. 일단 프로젝트가 준비되었습니다.
Gradle 스크립트 폴더로 이동하여 build.gradle(프로젝트: 앱)을 엽니다.


 ext.kotlin_version = "1.4.21"
    repositories {
        google()
        jcenter()
        maven {url 'https://developer.huawei.com/repo/'}
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.0.1"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.huawei.agconnect:agcp:1.4.2.300'


HMS Loaction 및 맵 키트에 대한 다음 종속성 추가

 // Huawei Map
    implementation 'com.huawei.hms:maps:6.2.0.301'
// Huawei Location Kit
    implementation 'com.huawei.hms:location:6.2.0.300'


Gradle 스크립트 폴더로 이동하여 build.gradle(모듈: 앱)을 엽니다.

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.huawei.agconnect'
apply plugin: 'kotlin-kapt'


android {
    compileSdkVersion 31
    buildToolsVersion "29.0.3"

    buildFeatures {
        dataBinding = true
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }

    defaultConfig {
        applicationId "com.hms.corrierapp"
        minSdkVersion 27
        targetSdkVersion 31
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

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

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    implementation "android.arch.lifecycle:extensions:1.1.1"

    implementation 'com.google.android.material:material:1.2.0'

    implementation 'com.squareup.retrofit2:retrofit:2.6.2'
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
    implementation 'com.squareup.retrofit2:converter-scalars:2.5.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2'
    implementation(
            [group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.4.1'],
            [group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.4.1'],
            [group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.4.1'],
    )

    //HMS Kits
    implementation 'com.huawei.agconnect:agconnect-core:1.5.0.300'
    implementation 'com.huawei.hms:hwid:5.3.0.302'

    implementation 'com.huawei.hms:push:4.0.3.301'

    implementation 'com.huawei.agconnect:agconnect-cloud-database:1.5.0.300'
    implementation "com.huawei.agconnect:agconnect-auth-huawei:1.6.0.300"
    implementation 'com.huawei.agconnect:agconnect-auth:1.5.0.300'

    // Huawei Map
    implementation 'com.huawei.hms:maps:6.2.0.301'
// Huawei Location Kit
    implementation 'com.huawei.hms:location:6.2.0.300'

}


코드 구현
다음 패키지 모델인 push, viewmodel을 생성했습니다.
모델: 기본 폴더에서 새 패키지를 만들고 이름을 모델로 지정합니다.

MapActivity.kt:

package com.hms.corrierapp.map

import android.Manifest
import android.content.IntentSender
import android.content.pm.PackageManager
import android.location.Location
import android.os.*
import android.util.Log
import android.widget.Chronometer
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import com.hms.corrierapp.R
import com.huawei.hmf.tasks.Task
import com.huawei.hms.common.ApiException
import com.huawei.hms.common.ResolvableApiException
import com.huawei.hms.location.*
import com.huawei.hms.maps.*
import com.huawei.hms.maps.model.*
import java.io.File
import java.text.DecimalFormat


class MapActivity : AppCompatActivity(), OnMapReadyCallback {

    private lateinit var mMapView: MapView
    private var mHwMap: HuaweiMap? = null
    private var mPolylineOptions: PolylineOptions? = null
    private var mListener: LocationSource.OnLocationChangedListener? = null

    private var mMarkerStart: Marker? = null
    private var mMarkerEnd: Marker? = null
    private var mLocationRequest: LocationRequest? = null
    private var mTvStart: TextView? = null
    private var mTvDistance: TextView? = null
    private var mTime: Chronometer? = null
    private var fusedLocationProviderClient: FusedLocationProviderClient? = null
    private val mPath: PathBean = PathBean()
    private var mSeconds: Long = 0
    private val mHandler = Handler(Looper.getMainLooper())
    private val mDecimalFormat = DecimalFormat("0.00")
    private var mIsRunning = false
    private val mTimeRunnable: Runnable = object : Runnable {
        override fun run() {
            mTime!!.text = formatSeconds()
            mHandler.postDelayed(this, 1000)
        }
    }
    private var mLocationCallback: LocationCallback? = null

    // GPS Data
    private var mGpsDataThread: HandlerThread? = null
    private var mGpsDataHandler: Handler? = null
    private var mGpsDataFile: File? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_map)
        checkPermission()
        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
        // check location settings
        checkLocationSettings()
        // init MapView
        mMapView = findViewById(R.id.hw_mapview)
        var mapViewBundle: Bundle? = null
        if (savedInstanceState != null) {
            mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY)
        }
        mMapView.onCreate(mapViewBundle)
        mMapView.getMapAsync(this)
        mTvDistance = findViewById(R.id.tv_distance)
        mTime = findViewById(R.id.cm_time)
        mTvStart = findViewById(R.id.tv_start)
        mTvStart!!.setOnClickListener({ processStartClick() })
        // Initializing Map Kit Polyline
        mPolylineOptions = PolylineOptions()
        mPolylineOptions!!.color(resources.getColor(R.color.colorAccent))
        mPolylineOptions!!.width(5f)
        // Recording GPS Data
        mGpsDataFile = File(getExternalFilesDir(null), "GpsData.txt")

    }

    private fun checkPermission() {
        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
            ) {
                val strings = arrayOf(
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION
                )
                ActivityCompat.requestPermissions(this, strings, 1)
            }
        } else {
            // Dynamically apply for permissions required for SDK > 28. Add the android.permission.ACCESS_BACKGROUND_LOCATION permission.
            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,
                    Manifest.permission.ACCESS_BACKGROUND_LOCATION
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                val strings = arrayOf(
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION,
                    Manifest.permission.ACCESS_BACKGROUND_LOCATION
                )
                ActivityCompat.requestPermissions(this, strings, 2)
            }
        }
    }

    override fun onMapReady(huaweiMap: HuaweiMap?) {
        Log.d(TAG, "onMapReady: ")
        mHwMap = huaweiMap
        mHwMap?.isMyLocationEnabled = true
        mHwMap?.uiSettings?.isZoomControlsEnabled = false
        // Add Location Source
        mHwMap?.setLocationSource(object : LocationSource {
            override fun activate(onLocationChangedListener: LocationSource.OnLocationChangedListener?) {
                mListener = onLocationChangedListener
            }

            override fun deactivate() {}
        })
        // Obtains the current position and updates the map camera.
        try {
            val lastLocation: Task<Location> = fusedLocationProviderClient!!.lastLocation
            lastLocation.addOnSuccessListener { location ->
                if (location ==null) return@addOnSuccessListener
                    mListener!!.onLocationChanged(location)
                if (mListener != null) {
                    mListener!!.onLocationChanged(location)
                    val cameraUpdate: CameraUpdate = CameraUpdateFactory.newLatLngZoom(
                        LatLng(location.latitude, location.longitude), 15f
                    )
                    mHwMap!!.animateCamera(cameraUpdate)
                }
            }.addOnFailureListener {
                Log.d(TAG, "onMapReady: Obtains the current position failure")
            }
        } catch (e: Exception) {

        }
    }

    companion object {
        private const val MAPVIEW_BUNDLE_KEY = "MapViewBundleKey"
        private const val TAG = "MapActivity"
    }

    override fun onStart() {
        super.onStart()
        mMapView.onStart()
    }

    override fun onStop() {
        super.onStop()
        mMapView.onStop()
    }

    override fun onDestroy() {
        removeLocationUpdatesWithCallback()
        super.onDestroy()
        mHandler.removeCallbacksAndMessages(null)
        mMapView.onDestroy()
    }

    override fun onPause() {
        mMapView.onPause()
        super.onPause()
        mGpsDataThread!!.quitSafely()
        try {
            mGpsDataThread!!.join()
            mGpsDataThread = null
            mGpsDataHandler = null
        } catch (e: InterruptedException) {
            e.printStackTrace()
        }
    }

    override fun onResume() {
        super.onResume()
        mMapView.onResume()
        mGpsDataThread = HandlerThread("DotThread")
        mGpsDataThread!!.start()
        mGpsDataHandler = Handler(mGpsDataThread!!.looper)
    }

    override fun onLowMemory() {
        super.onLowMemory()
        mMapView.onLowMemory()
    }

    private fun checkLocationSettings() {
        val builder: LocationSettingsRequest.Builder = LocationSettingsRequest.Builder()
        val locationSettingsRequest: LocationSettingsRequest = builder.build()
        val settingsClient: SettingsClient = LocationServices.getSettingsClient(this)
        settingsClient.checkLocationSettings(locationSettingsRequest)
            .addOnSuccessListener { requestLocationUpdate() }.addOnFailureListener { e ->
                when ((e as ApiException).statusCode) {
                    LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
                        val rae: ResolvableApiException = e as ResolvableApiException
                        rae.startResolutionForResult(this@MapActivity, 0)
                    } catch (sie: IntentSender.SendIntentException) {
                    }
                }
            }
    }

    private fun requestLocationUpdate() {
        mLocationRequest = LocationRequest()
        mLocationRequest!!.interval = 5000
        mLocationRequest!!.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        mLocationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                super.onLocationResult(locationResult)
                writeGpsData2Sdcard(locationResult.lastLocation)
                if (mIsRunning) {
                    processLocationChange(locationResult.lastLocation)
                }
            }

            override fun onLocationAvailability(locationAvailability: LocationAvailability?) {
                super.onLocationAvailability(locationAvailability)
            }
        }
        fusedLocationProviderClient
            ?.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.getMainLooper())
            ?.addOnSuccessListener { Log.i(TAG, "request location updates success") }
            ?.addOnFailureListener { e ->
                Log.e(TAG, "request location updates failed, error: " + e.message)
            }
    }

    // Removed when the location update is no longer required.
    private fun removeLocationUpdatesWithCallback() {
        try {
            val voidTask: Task<Void> =
                fusedLocationProviderClient!!.removeLocationUpdates(mLocationCallback)
            voidTask.addOnSuccessListener { }.addOnFailureListener { }
        } catch (e: Exception) {
            Log.e(TAG, "removeLocationUpdatesWithCallback Exception : $e")
        }
    }

    private fun processStartClick() {
        if (mIsRunning) {
            mIsRunning = false
            mPath.endTime = (System.currentTimeMillis())
            mTvStart!!.text = "Start"
            mHandler.removeCallbacks(mTimeRunnable)
            if (mPath.mPathLinePoints!!.size > 0) {
                mPath.endPoint = (mPath.mPathLinePoints!!.get(mPath.mPathLinePoints!!.size - 1))
                if (null != mMarkerStart && null != mMarkerEnd) {
                    mMarkerStart!!.remove()
                    mMarkerEnd!!.remove()
                }
                val startPointOptions: MarkerOptions = MarkerOptions()
                    .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_location_marker))
                    .position(mPath.mStartPoint)
                startPointOptions.title("Start Point")
                startPointOptions.snippet("Start Point")
                mMarkerStart = mHwMap!!.addMarker(startPointOptions)
                val endPointOptions: MarkerOptions = MarkerOptions()
                    .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_location_marker))
                    .position(mPath.mEndPoint)
                endPointOptions.title("End Point")
                endPointOptions.snippet("End Point")
                mMarkerEnd = mHwMap!!.addMarker(endPointOptions)
            }
        } else {
            mIsRunning = true
            mPath.reset()
            mPath.startTime = System.currentTimeMillis()
            mHandler.post(mTimeRunnable)
            mTvStart!!.text = "Stop"
        }
    }

    private fun processLocationChange(location: android.location.Location) {
        val latLng = LatLng(location.latitude, location.longitude)
        if (mPath.mStartPoint == null) {
            mPath.mStartPoint = latLng
        }
        mPath.addPoint(latLng)
        val distance: Float = mPath.updateDistance()
        val sportMile = distance / 1000.0
        if (mSeconds > 0) {
            val distribution = mSeconds.toDouble() / 60.0 / sportMile
            mPath.setDistribution(distribution)
            mTvDistance!!.text = mDecimalFormat.format(sportMile)
        } else {
            mPath.setDistribution(0.0)
            mTvDistance!!.text = "0.00"
        }
        mPolylineOptions!!.add(latLng)
        mHwMap!!.addPolyline(mPolylineOptions)

        if (mListener != null) {
            mListener!!.onLocationChanged(location)
            val cameraUpdate: CameraUpdate = CameraUpdateFactory.newLatLngZoom(
                LatLng(location.latitude, location.longitude), 15f
            )
            mHwMap!!.animateCamera(cameraUpdate)
        }
    }

    fun formatSeconds(): String {
        val hh = if (mSeconds / 3600 > 9) mSeconds / 3600 else mSeconds / 3600
        val mm =
            if (mSeconds % 3600 / 60 > 9) mSeconds % 3600 / 60 else mSeconds % 3600 / 60
        mSeconds++
        return "$hh:$mm"
    }

    private fun writeGpsData2Sdcard(location: Location) {
        Log.d(
            TAG,
            "write latitude and longitude, latitude: " + location.latitude + ", longitude: " + location.longitude
        )
        mGpsDataHandler!!.post(
            GpsDataSaver(
                mGpsDataFile, """
     ${location.latitude}, ${location.longitude}

     """.trimIndent()
            )
        )
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        }
    }


}


activity_map.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:map="http://schemas.android.com/apk/res-auto"
    android:id="@+id/sport_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.huawei.hms.maps.MapView
        android:id="@+id/hw_mapview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        map:mapType="normal"
        map:uiCompass="true"
        map:uiZoomControls="true" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_marginStart="15dp"
        android:layout_marginTop="60dp"
        android:layout_marginEnd="15dp"
        android:background="@color/white"
        android:orientation="horizontal">

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="20dp"
            android:layout_marginTop="20dp"
            android:layout_weight="1"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv_distance"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:gravity="center_horizontal"
                android:maxLength="8"
                android:text="0.00"
                android:textColor="#000000"
                android:textSize="25sp"
                android:textStyle="bold" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="6dp"
                android:layout_marginBottom="2.5dp"
                android:gravity="center_horizontal"
                android:text="km"
                android:textColor="#88000000"
                android:textSize="18sp" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="20dp"
            android:layout_marginTop="20dp"
            android:layout_weight="2"
            android:orientation="vertical">

            <Chronometer
                android:id="@+id/cm_time"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="20dp"
                android:ellipsize="end"
                android:format="00:00"
                android:gravity="center"
                android:textColor="#000000"
                android:textSize="22sp"
                android:textStyle="bold" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="6dp"
                android:layout_marginBottom="2.5dp"
                android:gravity="center_horizontal"
                android:text="Total time"
                android:textColor="#88000000"
                android:textSize="18sp" />
        </LinearLayout>

    </LinearLayout>

    <TextView
        android:id="@+id/tv_start"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="30dp"
        android:gravity="center"
        android:text="Track Courier Location"
        android:textColor="@color/colorPrimary"
        android:textSize="21sp"
        android:textStyle="bold" />

</RelativeLayout>


activity_delivery_status.xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white">

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:padding="16dp">


                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:padding="5dp"
                    android:text="@string/delivery"
                    android:textAlignment="center"
                    android:textColor="@color/colorPrimaryDark"
                    android:textSize="34sp"
                    android:textStyle="bold" />

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:orientation="horizontal">

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="100dp"
                        android:layout_weight="0.1"
                        android:gravity="center"
                        android:orientation="vertical">

                        <ImageView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:background="@drawable/ic_location_marker" />

                        <View
                            android:layout_width="5dp"
                            android:layout_height="match_parent"
                            android:background="@android:color/black" />

                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="100dp"
                        android:layout_weight="0.9"
                        android:gravity="center_vertical"
                        android:orientation="vertical">

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="Booking"
                            android:textAlignment="center"
                            android:textColor="@color/gray"
                            android:textSize="20sp"
                            android:textStyle="bold" />

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:padding="5dp"
                            android:text="Done"
                            android:textAlignment="center"
                            android:textColor="@color/green"
                            android:textSize="18sp"
                            android:textStyle="bold" />


                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:padding="5dp"
                            android:text="Payment Pending"
                            android:textAlignment="center"
                            android:textColor="@color/red"
                            android:textSize="18sp"
                            android:textStyle="bold" />

                    </LinearLayout>

                </LinearLayout>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:orientation="horizontal">

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="100dp"
                        android:layout_weight="0.1"
                        android:gravity="center"
                        android:orientation="vertical">

                        <ImageView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:background="@drawable/ic_location_marker" />

                        <View
                            android:layout_width="5dp"
                            android:layout_height="match_parent"
                            android:background="@android:color/black" />

                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="100dp"
                        android:layout_weight="0.9"
                        android:gravity="center_vertical"
                        android:orientation="vertical">

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="PickUp"
                            android:textAlignment="center"
                            android:textColor="@color/gray"
                            android:textSize="20sp"
                            android:textStyle="bold" />

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:padding="5dp"
                            android:text="Done"
                            android:textAlignment="center"
                            android:textColor="@color/green"
                            android:textSize="18sp"
                            android:textStyle="bold" />


                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:padding="5dp"
                            android:text="Shipping"
                            android:textAlignment="center"
                            android:textColor="@color/gray"
                            android:textSize="18sp"
                            android:textStyle="bold" />

                    </LinearLayout>

                </LinearLayout>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:orientation="horizontal">

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="100dp"
                        android:layout_weight="0.1"
                        android:gravity="center"
                        android:orientation="vertical">

                        <ImageView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:background="@drawable/ic_location_marker" />

                        <View
                            android:layout_width="5dp"
                            android:layout_height="match_parent"
                            android:background="@android:color/black" />

                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="100dp"
                        android:layout_weight="0.9"
                        android:gravity="center_vertical"
                        android:orientation="vertical">

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="Delivery"
                            android:textAlignment="center"
                            android:textColor="@color/gray"
                            android:textSize="20sp"
                            android:textStyle="bold" />

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:padding="5dp"
                            android:text="On It's Way"
                            android:textAlignment="center"
                            android:textColor="@color/gray"
                            android:textSize="18sp"
                            android:textStyle="bold" />


                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:padding="5dp"
                            android:text="Tracking ID: 12231223"
                            android:textAlignment="center"
                            android:textColor="@color/gray"
                            android:textSize="18sp"
                            android:textStyle="bold" />

                    </LinearLayout>

                </LinearLayout>


                <Button
                    android:id="@+id/btn_done"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="20dp"
                    android:layout_marginBottom="5dp"
                    android:background="@color/colorPrimaryDark"
                    android:text="See On Map"
                    android:textColor="@color/white"
                    android:textStyle="bold" />
            </LinearLayout>

        </ScrollView>

    </RelativeLayout>
</layout>


앱 빌드 결과

팁과 요령

minSDK 버전을 24 이상으로 설정하십시오. 그렇지 않으면 AndriodManifest 병합 문제가 발생합니다.

앱 폴더에 agconnect-services.json 파일을 추가했는지 확인합니다.

반드시 SHA-256 지문을 추가했는지 확인하십시오.

모든 종속성이 제대로 추가되었는지 확인하십시오.

결론

이 기사에서는 Android 애플리케이션에서 HMS 계정, 푸시, CloudDB, AuthService, 푸시 키트 업링크 메시지, 위치 및 맵 키트를 통합하는 방법을 배웠습니다. 이 기사를 완전히 읽은 후 사용자는 Kotlin을 사용하여 Courier Android 애플리케이션에서 Location 및 Map Kit를 쉽게 구현할 수 있습니다.

이 기사를 읽어 주셔서 감사합니다. 이 글이 도움이 되셨다면 좋아요와 댓글을 꼭 남겨주세요. 그건 나에게 큰 의미 야.

참조

HMS 문서:

https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/introduction-0000001050048870

https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-sdk-brief-introduction-0000001061991343

https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-introduction-0000001121930588

위치 및 지도 키트 교육 비디오:

https://developer.huawei.com/consumer/en/training/course/video/201575277450653242

좋은 웹페이지 즐겨찾기