ロコガイド テックブログ

「地域のくらしを、かしこく、たのしく」する、株式会社ロコガイドの技術部ブログです。主にトクバイ・ロコナビのサービス開発について発信しています。

「地域のくらしを、かしこく、たのしく」する、株式会社ロコガイドの技術部ブログです。
主にトクバイ・ロコナビのサービス開発について発信しています。

トクバイアプリの「Android11」対応まとめ

f:id:yokomii:20200923135716p:plain

Androiderのみなさんこんにちは
ロコガイドでAndroidエンジニアをやってます横山(yokomii)です。

Android11が正式リリースされて数日がたちますが、TargetSDKのバージョンアップ対応は進んでおりますでしょうか?
トクバイアプリも絶賛進行中なのですが、おおよその修正対応範囲や方針が見えてきたので、今回はその内容をシェアしたいと思います。

あくまでトクバイアプリ上での対応内容ですので、
各自で開発中のアプリに必要な対応内容に関しては動作の変更点: Android 11 をターゲットとするアプリなどをご確認ください。

(具体的な対応例はいくらあっても困らないだろうという考えのもとにブログを書いてます。)

Package Visibilityの変更

TargetSDKが30かつAndroid11のとき、端末にインストールされているその他アプリ(Intent)情報へのアクセスが厳格になりました。

ref: Android 11 でのパッケージへのアクセス

具体的には

  • PackageManage.getInstalledApplications()
  • PackageManage.getInstalledPackages()

で取得できるアプリケーションやパッケージの情報が、一部のアプリ(設定アプリなど)を除いてフィルタされた状態で返るようになっていたり

  • Intent.resolveActivity()
  • PackageManager.queryIntentActivities()

で取得できるActivityの情報も、一部アプリを除いて制限されたりしています。

トクバイアプリにはPackageManager.queryIntentActivities()で、Intentに対応するActivityを確認した後、startActity()を実行するようなコードが点在しています。

以下がその例です。

val intent = Intent(
    Intent.ACTION_DIAL, 
    Uri.parse("tel:090XXXXXXXX")
)
val existsActivity = packageManager.queryIntentActivities(
        intent, 
        PackageManager.MATCH_DEFAULT_ONLY
    ).isNotEmpty()

if(existsActivity) {
    startActivity(intent)
} else {
    Toast.makeText(this, "対応するアプリがありません", Toast.LENGTH_SHORT).show()
}

上記コードはAndroid10以前ではstartActivity()が開始されますが、Android11では対応するアプリが見つからずにエラーメッセージが表示されます。

エラーを回避する方法として、AndroidManifestに<queries>要素を定義します

AndroidManifest.xml

<manifest package="jp.co.tokubai.example">
    <queries>
        <intent>
            <action android:name="android.intent.action.DIAL" />
            <data android:scheme="tel" />
        </intent>
    </queries>
    ...
</manifestt>

<queries>要素にIntentを指定することで、Android10以前と同様に対応するアプリを見つけることができます。

その他の対処方法

インストールされているアプリの情報がわからなくても暗黙的インテントの呼び出しは今まで通り行えるので

PackageManager.queryIntentActivities()で、Intentに対応するActivityを確認した後、startActity()を実行するようなコード

のようなケースは、ActivityNotFoundExceptionをハンドリングする方法に置き換えることもできます。

val intent = Intent(
    Intent.ACTION_DIAL, 
    Uri.parse("tel:090XXXXXXXX")
)

try {
    context.startActivity(intent))
} catch (e: ActivityNotFoundException) {
    Toast.makeText(this, "対応するアプリがありません", Toast.LENGTH_SHORT).show()
}

Custom Tabs

CustomTabsをアプリで使用している場合、対応するブラウザアプリもフィルタされているため、タブが立ち上がらなくなります。

ref: Using Custom Tabs with Android 11

android.support.customtabs.action.CustomTabsServiceのactionをqueriesに定義することでこれを回避できます。

<queries>
    <intent>
        <action android:name="android.support.customtabs.action.CustomTabsService" />
    </intent>
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.BROWSABLE" />
    </intent>
</queries>

トクバイアプリではCustomTabsに対応するブラウザがないときに、その他のブラウザを探して起動する処理を行っているので、android.intent.category.BROWSABLEのintentも検出対象としています。

バックグラウンド位置情報アクセスの変更

Android11では位置情報のアクセス処理が以下のように変更されています。

  • 位置情報を1回だけ許可(毎回確認)できるようになった
    • Android10では「常に許可」「使用中のみ許可」「許可しない」の3段階だったのが、Android11では「常に許可」「使用中のみ許可」「毎回確認」「許可しない」の4段階に増えた
  • パーミッションダイアログ上から次回以降表示しないオプション(チェックボックス)がなくなった
    • 複数回拒否すると自動的にダイアログを表示しない設定になる
    • 位置情報以外のパーミッション取得時も共通
  • フォアグラウンド位置情報(使用中のみ許可)が許可されていないとき、バックグラウンド位置情報(常に許可)がとれなくなった
    • フォアグラウンド⇨バックグラウンドと段階的に許可をとる必要がある
    • TargetSDKが30未満の場合はフォアグラウンドとバックグラウンドを同時にとれる
  • バックグラウンド位置情報取得時にパーミッションダイアログが表示されなくなり、アプリの設定画面に遷移するようになった

f:id:yokomii:20200923135740p:plain:w200

ref: Android 11 での位置情報に関する更新

トクバイアプリでは

フォアグラウンド位置情報(使用中のみ許可)が許可されていないとき、バックグラウンド位置情報(常に許可)がとれなくなった

の変更に対して対応を行いました。

位置情報の段階的許可

TargetSDKが29のとき、バックグラウンドとフォアグラウンドの位置情報を同時にリクエストし、バックグラウンド位置情報が許可されると
grantResultsはすべてPERMISSION_GRANTEDが返っていました。

private fun request() {
    ActivityCompat.requestPermissions(
        this, 
        arrayOf(
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_BACKGROUND_LOCATION
        ), 
        requestCode
    )
}

override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        grantResults[0] // value is PermissionChecker.PERMISSION_GRANTED(0)
        grantResults[1] // value is PermissionChecker.PERMISSION_GRANTED(0)
    }

対してTargetSDKが30のときは、ACCESS_FINE_LOCATIONの対応分がPERMISSION_DENIEDでレスポンスされます。

override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        grantResults[0] // value is PermissionChecker.PERMISSION_DENIED(-1)
        grantResults[1] // value is PermissionChecker.PERMISSION_GRANTED(0)
    }

トクバイアプリではgrantResultsがすべてgrantedのときに後続処理(バックグラウンド位置情報取得の開始)を開始していたため、修正が必要でした。

修正後は、フォアグラウンドとバックグラウンドの処理を分解しました。
バックグラウンド位置情報取得前には、フォアグラウンド位置情報が許可されているかを確認するようにし、そうでなければバックグラウンド位置情報を取得しないようにしました。

if(
    ContextCompat.checkSelfPermission(application, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
) {
    ActivityCompat.requestPermissions(
        this, 
        arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), 
        requestCode
    )
}

フォアグラウンド位置情報が許可されていないときにrequestPermissions()を実行すると、PERMISSION_DENIEDが返ります。

Text Toast API の変更

Android11かつTargetSDKが30のとき、カスタムビューを含むトーストの制限や、テキストトーストのAPIが一部機能しなくなります。

ref: Android 11 でのトーストに関する更新

トクバイアプリではsetGravity()でトーストの表示位置を一部調整していたので、該当部分をカスタムビューに置き換えるか、Snackbarに置き換えるかを検討中です。

まとめ

以上がトクバイアプリでの主な対応内容です。
プライバシー周りの変更対応が若干難儀ですが、いずれ向き合わなければいけないときが来るので、なるはやで対応しておきたいですね。

移行も済んだ(済みそうな)ことですし、チャットバブルなどの新機能にも積極的に取り組んでいけたらなと思います😘


ロコガイドではスピード感を持ってSDK移行できる仲間を募集しています!(気概があればOK!)
下記のリンクからお気軽にお問い合わせください〜🏃‍♀️