🗺️

JetpackComposeでGoogleMap埋め込んでみた

2022/04/01に公開

https://cloud.google.com/blog/products/maps-platform/compose-maps-sdk-android-now-available

こんな感じのものが出ていたので試してみました。

マップの埋め込み自体はこちらの記事のままで

本当に数行書くだけでできてしまったのでめちゃくちゃ簡単で驚きました。

しかし、

Composeが全く関係ない自分で現在地を取得するなど処理が

死ぬほど難解だったのでまとめておきます(未だによくわかっていない)

現在位置取得処理

パーミッションの設定

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

マニフェストにこれを追加する。

COARSEがローレベル検索、FINEがハイレベルな検索をしてくれる模様

パーミッションの状態確認

よくある『アプリがhogehogeを要求しています。許可しますか?』

みたいなダイアログを出すやつです。

ContextCompat.checkSelfPermission()

というものを使って許可されているかいないかを確認することができるようです。

ダイアログはこちらの

ActivityCompat.requestPermissions()

を使うことで表示できるようです。

全部まとめるとこんな感じ

private val REQUEST_CODE_LOCATION = 100

if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
//現在地取得処理を書く
        }
        else {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_CODE_LOCATION)
        }
  1. ContextCompat.checkSelfPermission()で判定
  2. 許可されていなければActivityCompat.requestPermissions()でダイアログ表示
  3. ActivityCompat.requestPermissions()は引数に
    context,arrayOf(必要なパーミッション),固有のリクエストコードを設定して送る形になります。

固有のリクエストコードはコールバックされて返ってくるので

その際の判定にも使用できるみたい。

現在地取得処理

fusedLocationClient = FusedLocationProviderClient(requireActivity())

        // どのような取得方法を要求
        val locationRequest = LocationRequest.create().apply {
            interval = 10000                                   // 最遅の更新間隔(但し正確ではない。)
            fastestInterval = 5000                             // 最短の更新間隔
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY  // 精度重視
        }

        // コールバック
        val locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                //locationResultに結果が入っているので後はよしなにする
		
		//removeLocationUpdatesをしないと永遠と位置情報を取得し続けるので消してあげる
                fusedLocationClient.removeLocationUpdates(this)
            }
        }

// 取得処理
fusedLocationClient.requestLocationUpdates(
                                        locationRequest,
                                        locationCallback,
                                        Looper.myLooper()!!
                                    )
  1. fusedLocationClientは取得関連を行うためのインスタンス
  2. LocationRequest.create()で色々設定する
  3. fusedLocationClient.requestLocationUpdates()で取得処理
  4. locationCallbackを通ってonLocationResult()に結果が返ってくるのでよしなにする
  5. 最後にfusedLocationClient.removeLocationUpdates()で終了処理をする。

許可されない場合のハンドリングを作る

private fun locationStart(
        getLocation: () -> Unit
    ) {
        // Instances of LocationManager class must be obtained using Context.getSystemService(Class)
        val locationManager =
            requireActivity().getSystemService(LOCATION_SERVICE) as LocationManager

        if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
            Timber.d("debug", "location manager Enabled")
        } else {
            // to prompt setting up GPS
            val settingsIntent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
            startActivity(settingsIntent)
            Timber.d("debug", "not gpsEnable, startActivity")
        }
        if (ContextCompat.checkSelfPermission(
                requireContext(),
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            ActivityCompat.requestPermissions(
                requireActivity(),
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_CODE_LOCATION
            )
            Timber.d("debug", "checkSelfPermission false")
            showDialogToGetPermission()
            return
        }

        getLocation()
    }
    
private fun showDialogToGetPermission() {
        val builder = AlertDialog.Builder(requireContext())
        builder.setTitle("現在地取得が利用できません")
            .setMessage("現在地取得を利用するには権限を許可してください")

        builder.setPositiveButton("OK") { _, _ ->
            val intent = Intent(
                Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                Uri.fromParts("package", "hogehoge", null)
            )
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            startActivity(intent)   // 6
        }
        builder.setNegativeButton("CANCEL") { _, _ ->
            // ignore
        }
        val dialog = builder.create()
        dialog.show()
    }
  1. ActivityCompat.requestPermissions()の後にlocationStart()する
  2. 拒否された場合showDialogToGetPermission()が発火してアプリ設定に遷移させることができる
  3. この導線を作らないと2回以上拒否されると2度とダイアログが出てこなくなるのでとても困る
  4. 引数にはfusedLocationClient.requestLocationUpdates()を渡してあげる

参考記事

https://cloud.google.com/blog/products/maps-platform/compose-maps-sdk-android-now-available

https://developer.android.com/training/location/retrieve-current?hl=ja

https://hirauchi-genta.com/kotlin-location-permission/

https://px-wing.hatenablog.com/entry/2021/03/22/065434

https://medium.com/@hasperong/get-current-location-with-latitude-and-longtitude-using-kotlin-2ef6c94c7b76

https://stackoverflow.com/questions/51313359/get-current-location-android-kotlin

https://akira-watson.com/android/gps.html

https://codechacha.com/ja/android-get-location-from-locationmanager/

https://blog.dtdweb.com/2013/04/06/gps_desc/

https://www.tabnine.com/code/java/methods/android.location.LocationManager/requestLocationUpdates

https://akira-watson.com/android/kotlin/gps-simple.html

https://codechacha.com/ja/android-request-runtime-permissions/

https://tail-island.github.io/programming/2017/06/19/android-permission.html

Discussion