アプリのAndroid 11対応で行ったこと

okhttpのバージョンを上げた

上げないとsetSocketFactoryできない
https://gist.github.com/tateisu/51d20ef6aa6c813fab5635f33880bdfb

ToastCompatを書き直した

Android 11でToastにカスタムビューを設定できる場所が制限され、またカスタムビューを設定しない限り Toast.getView() はnullを返す。
APIレベル25にはアプリのコードと関係ないところでToastがクラッシュするバグがあり、それを回避する ToastCompatというライブラリがある。
Toast.getView() が null を返す場合でもうまく動作するようToastCompatを書き直した。
投げたissueに代替コードも載せてある。
github.com

AndroidManifest.xmlに queries セクションを書いた

targetSdkVersion 30 以降で、PackageManager の resolveActivity() 、 queryIntentActivities()、 getInstalledApplications() 、 getInstalledPackages() などの他のパッケージを探索するAPIを使う際は AndroidManifest.xml に対象を明記しないといけない。

developer.android.com

例。 利用できるブラウザを確認する、Chrome Custom Tabs の有無を確認する、テキストを共有できるアプリの一覧を調べる、読み上げの利用、Simejiのマッシュルームプラグインの存在確認、など。

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ... >
    <!--suppress AndroidElementNotAllowed -->
    <queries>

        <intent>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.BROWSABLE" />
        </intent>

        <intent>
            <action android:name="android.support.customtabs.action.CustomTabsService" />
        </intent>

        <intent>
            <action android:name="android.intent.action.SEND" />
            <data android:mimeType="text/plain" />
        </intent>

        <intent>
            <action android:name="android.intent.action.TTS_SERVICE" />
        </intent>

        <intent>
            <action android:name="com.adamrocker.android.simeji.ACTION_INTERCEPT" />
            <category android:name="com.adamrocker.android.simeji.REPLACE"/>
        </intent>
    </queries>

    <application ... >
実行時権限の確認/取得でエラーがでた場合のメッセージを変更した

(targetSdkVersionに関わらず)Android 11は暫く使ってないアプリの実行時権限を削除する。
また実行時権限のリクエストをユーザが拒否したりすると権限ダイアログを表示しなくなる。
実行時権限のリクエストが拒否された際に、端末の設定画面から権限を与えるようにユーザを促すべき。
なおアプリの設定画面を開くインテントは中華系端末だと権限の設定ができない画面に飛ばされるので、今回はそういった誘導はしていない。

JobScheduler.schedule()が失敗したらlogcatに出力させた

(targetSdkVersionに関わらず) Android 11は JobScheduler.schedule() が呼び出された時にレートリミットを超えると実際には何もしないようになった。
アプリのマニフェストに debuggable 属性がセットされている場合、JobScheduler.schedule() はRESULT_FAILURE を返す。

WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS と FLAG_TRANSLUCENT_NAVIGATION

ステータスバーとナビゲーションバーを不透明にしたい場合、Android 11以降の端末では以下の呼び出しが不要になる。

if(Build.VERSION.SDK_INT < 30) {
  @Suppress("DEPRECATION")
  clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS or WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
}
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR と View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR

ステータスバーとナビゲーションバーのアイコン色を調整したい場合、端末のバージョンによって処理を変える。

activity.window?.apply {
	statusBarColor = c1 or Color.BLACK
	if(Build.VERSION.SDK_INT >= 30) {
		decorView.windowInsetsController?.run {
			val bit = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
			setSystemBarsAppearance(if(rgbToLab(c).first >= 50f) bit else 0, bit)
		}
	} else if(Build.VERSION.SDK_INT >= 23) {
		@Suppress("DEPRECATION")
		val bit = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
		@Suppress("DEPRECATION")
		decorView.systemUiVisibility =
			if(rgbToLab(c).first >= 50f) {
				//Dark Text to show up on your light status bar
				decorView.systemUiVisibility or bit
			} else {
				//Light Text to show up on your dark status bar
				decorView.systemUiVisibility and bit.inv()
			}
	}
	
	navigationBarColor = c2 or Color.BLACK
	if(Build.VERSION.SDK_INT >= 30) {
		decorView.windowInsetsController?.run {
			val bit = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS
			setSystemBarsAppearance(if(rgbToLab(c).first >= 50f) bit else 0, bit)
		}
	} else if(Build.VERSION.SDK_INT >= 26) {
		@Suppress("DEPRECATION")
		val bit = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
		@Suppress("DEPRECATION")
		decorView.systemUiVisibility =
			if(rgbToLab(c).first >= 50f) {
				//Dark Text to show up on your light status bar
				decorView.systemUiVisibility or bit
			} else {
				//Light Text to show up on your dark status bar
				decorView.systemUiVisibility and bit.inv()
			}
	}
}



アプリによって対応が必要な範囲は異なると思うけど、とりあえず今回はここまで。

おまけ

developer.android.com

COVID-19関連の考慮事項により、Android 11(APIレベル30)以上を対象とし、MANAGE_EXTERNAL_STORAGE 許可を必要とするアプリは、2021年の初めまでGoogle Playにアップロードできません。

ファイル管理をがっちりやるアプリは MANAGE_EXTERNAL_STORAGE 権限が必要なんで、これは困るやつ。コロナを理由にすれば何でも通ると思ってるなGoogleさん…