【Android】Android 14 新機能まとめ

## はじめに

一昨日 Android 14 Beta 5 がリリースされました。公式に`final compatibility testing`と記載され、最終リリースまであと僅かとなりました。そこで、今回は Android 14 で提供される新機能の概要をまとめてみました。

## 機能と API の概要

Android 14 の「新機能と APIの概要」は、[公式ページ](https://developer.android.com/about/versions/14/features?hl=ja)に記載されており、日本語の翻訳も徐々に進んでおりますが、ニュアンスが微妙なところもあるので、可能であれば[英語版](https://developer.android.com/about/versions/14/features?hl=en)で読むとより理解が進むかと思います。

Android 14 の新機能を大別すると、以下の 3 つに分類されるので、それぞれを順に紹介したいと思います。

* 国際化（Internationalization）
* ユーザー補正（Accessibility）
* ユーザー体験（User experience）


API の追加、変更、削除の一覧については、[API 差分レポート](https://developer.android.com/sdk/api_diff/u-dp1/changes?hl=ja)でご確認ください。また、新しい API についての詳細は、[Android API リファレンス](https://developer.android.com/reference?hl=ja)でご確認ください。

## 国際化（Internationalization）

国際化の新機能としては、`アプリ固有の言語設定の自動化`と `Grammatical Inflection API（文法的な語形変化）` が追加になります。

### アプリ固有の言語設定

Android 13（API レベル 33）で導入されたアプリ別の言語機能が拡張され、以下の機能が追加されます。

* アプリの `localeConfig` の自動生成
* アプリの `localeConfig` の動的アップデート
* インプット メソッド エディタ（IME）によるアプリの言語の確認


#### Android 13（API レベル 33） でのアプリ別言語機能

Android 13でアプリ固有の言語設定を行うためには、以下の設定をする必要がありました。

１. アプリでサポートする言語設定を locales_config.xml 追加する。

```xml:locales_config.xml
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
   <locale android:name="en-US"/>
   <locale android:name="en-GB"/>
   <locale android:name="fr"/>
   <locale android:name="ja"/>
   <locale android:name="zh-Hans-MO"/>
   <locale android:name="zh-Hant-MO"/>
</locale-config>
```

２. AndroidManifest に locales_config.xml を定義する。

```xml:AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
   <application
       android:localeConfig="@xml/locales_config">
   </application>
</manifest>
```

このように、アプリ固有の言語設定を行うためには、明示的にサポートする言語を設定する必要がありました。

#### Adnroid 14（API レベル 34） でのアプリ別言語機能

一方、Android 14では、locales_config.xml の生成を以下の設定を追加することで、自動生成にすることが可能です。なお、自動生成には、Android Studio Giraffe Canary 7 および AGP 8.1.0-alpha07 以降が必要です。

１. androidResources > generateLocaleConfig を定義する。

```gradle:build.gradle.kts
android {
   androidResources {
       generateLocaleConfig true
   }
}
```

２. resources.properties を作成し、デフォルトの言語を設定する。

```properties:res/resources.properties
unqualifiedResLocale=en-US
```

このように設定するだけで、locales_config.xml の生成を自動生成にすることができます。

### Grammatical Inflection API

Android 14 では、性別で文法が変わる言語に合わせてユーザー中心の UI を構築するため、アプリをリファクタリングせずに文法上の性別への対応を追加できる Grammatical Inflection API が導入されています。

日本語の場合、文法的な語形変化はなかなかイメージしにくいと思いますが、フランス語での一例を紹介します。

🇫🇷 冠詞の性別  ( 英語 :  the )
* 男性形単数 :  le
* 女性形単数 :  la

🇫🇷 動詞の過去分詞 ( 英語 :  spoken )
* 男性形単数 :  parlé 
* 女性形単数 :  parlée

Grammatical Inflection API では、このような文法的言語変化に対応することができます。

#### grammatical gender の設定方法

Grammatical Inflection API で設定できる文法上の性別の設定は以下の 3 パターンになります。

```java:Configration.java
public static final int GRAMMATICAL_GENDER_NEUTRAL   = 1; // 中性的
public static final int GRAMMATICAL_GENDER_FEMININE  = 2; // 女性的
public static final int GRAMMATICAL_GENDER_MASCULINE = 3; // 男性的
``` 

アプリで Grammatical Inflection API を利用するには、対象となる Activity に`configChanges` の指定します。

```xml:AndroidManifest.xml
<activity 
   android:name=".MainActivity"
   android:configChanges="grammaticalGender"
   android:exported="true">
</activity>
```

実際に grammatical gender を`GRAMMATICAL_GENDER_FEMININE`に設定する場合は、`setRequestedApplicationGrammaticalGender()` API を利用して設定します。

```kotlin
val gIM = context.getSystemService(GrammaticalInflectionManager::class.java)
gIM.setRequestedApplicationGrammaticalGender(Configuration.GRAMMATICAL_GENDER_FEMININE)
```

また、設定された grammatical gender を取得するには、`getApplicationGrammaticalGender()` API を利用します。

```kotlin
val gIM = context.getSystemService(GrammaticalInflectionManager::class.java)
val grammaticalGender = gIM.getApplicationGrammaticalGender()
```

このように設定した grammatical gender によって、

* -neuter
* -feminine
* -masculine

の suffix を付けた resources ファイルでリソースを使い分けることができるようになります。

```xml:res/values-fr-neuter/strings.xml
<resources>
   <string name="example_string">Abonnement à...activé</string>
</resources>
```

```xml:res/values-fr-feminine/strings.xml
<resources>
   <string name="example_string">Vous êtes abonnée à...</string>
</resources>
```

```xml:res/values-fr-masculine/strings.xml
<resources>
   <string name="example_string">Vous êtes abonné à...</string>
</resources>
```
このように Grammatical Inflection API では文法上の性別によって表現を変えることができるため、性別を扱うことが多い SNS アプリなどで活用できる場面もあるかと思いますが、ローカルでのリソース設定に止まるため使える場面は限定的になりそうです。

## ユーザー補正（Accessibility）

Android 14 では、非線形フォントスケーリングが 200% までサポートされます。SP 指定の場合、追加のオプションとスケーリングの改善がアプリのテキストに自動的に適用されるようになります。
![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2299135/f3a81f79-564a-7d42-c6ac-b1addb8264d3.png)
（引用: [The first developer preview of Android 14](https://android-developers.googleblog.com/2023/02/first-developer-preview-android14.html)）

このようにスタンダードスケーリングだと表示崩れが生じるため、`200%のフォントサイズで UI テストで確認すること`が重要となります。

200% のフォントサイズを有効にする手順は以下の通りです。

1. 設定アプリを開き、[ユーザー補助] > [表示サイズとテキスト] に移動]
1. [フォントサイズ] オプションで、最大フォントサイズの設定が有効になるまで、プラス（+）アイコンをタップ 

また、アプリで sp 単位を使用している場合、Android はユーザーが選択するテキストサイズを適用して適切にスケーリングできるよう、テキストサイズは必ず [sp 単位](https://developer.android.com/training/multiscreen/screendensities?hl=ja#TaskUseDP)で指定しましょう。

## ユーザー体験（User experience）

ユーザー体験では、以下の機能が追加になりました。

1. 共有シートのカスタムアクションの追加
1. アプリストアの改善
1. スクリーンショットの検知
1. 予測型「戻る」アプリ内アニメーション

### 共有シートのカスタムアクションの追加

共有シートのカスタムアクションを追加するには、カスタム ChooserAction から ChooserActions のリストを作成します。

```kotlin:MainActivity.kt
fun buildCustomShareSheetActions(context: Context): Array<ChooserAction> {
    val pendingIntent = PendingIntent.getActivity(
        context,
        0,
        Intent(Intent.ACTION_WEB_SEARCH).apply {
            putExtra(SearchManager.QUERY, "Search on browser")
        },
        PendingIntent.FLAG_IMMUTABLE
    )

    val actions = mutableListOf<ChooserAction>()

    for (i in 0 until 5) {
        val customAction = ChooserAction.Builder(
            Icon.createWithResource(context, R.drawable.ic_launcher_foreground),
            "Action${i+1}",
            pendingIntent
        ).build()
        actions.add(customAction)
    }

    return actions.toTypedArray()
}
```

次に、作成した ChooserActions のリストを Intent.EXTRA_CHOOSER_CUSTOM_ACTIONS として指定し、共有シートを開きます。


```kotlin:MainActivity.kt
fun buildChooserIntent(chooserActions: Array<ChooserAction>): Intent {
   val intent = Intent(Intent.ACTION_SEND).apply {
       type = "text/plain"
       putExtra(Intent.EXTRA_TEXT, "dummy text")
   }
   val chooserIntent = Intent.createChooser(intent, "Android 14").apply {
       putExtra(Intent.EXTRA_CHOOSER_CUSTOM_ACTIONS, chooserActions)
   }
   return chooserIntent
}

fun showActionSheet() {
   val chooserActions = buildCustomShareSheetActions(this)
   val chooserIntent = buildChooserIntent(chooserActions)
   startActivity(chooserIntent)
}
```

このように複数のカスタムアクションを設定することができます。

<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2299135/4b5ec501-2102-1851-a081-f77c99164d4d.png">


### アプリストアの改善

Android 14では、ユーザの操作を中断せずにアプリのアップデートが可能になります。具体的には、　以下の操作を API で実現できるようになります。

* ダウンロードする前にインストールの承認をリクエスト
* 今後の更新に責任を移譲
* 影響が少ないタイミングでアプリを更新

アプリインストール操作の事前承認では、targetSdk 34 で`PackageInstaller`が追加され、`requestUserPreapproval()`を実行することで、事前承認を行うことができます。

```java:PackageInstaller.java
public class PackageInstaller {
    public void requestUserPreapproval(@NonNull PreapprovalDetails details,
@NonNull IntentSender statusReceiver) {
    }
}
```

実際に承認する場合は、このようにリクエストを行います。

```kotlin
fun installApplication() {
   val packageInstaller = packageManager.packageInstaller
   val params = PackageInstaller.SessionParams(
       PackageInstaller.SessionParams.MODE_FULL_INSTALL
   )
   val sessionId = packageInstaller.createSession(params)
   val session = packageInstaller.openSession(sessionId)
   val statusReceiver = pendingIntent.intentSender
   val preapprovalDetail = PackageInstaller
       .PreapprovalDetails.Builder()
       .setLocale(ULocale.JAPAN)
       .setPackageName("com.example.app")
       .build()
   session.requestUserPreapproval(preapprovalDetail, statusReceiver)
}
```
アプリストアの改善機能は、スムーズなアプリインストールを促すことが期待できるため、ユーザーにとっても嬉しい機能になるでしょう。

### スクリーンショットの検知

API level 17 以降では、`FLAG_SECURE`を設定することでスクリーンショットを無効にすることができましたが、Android 14 ではスクリーンショットの検知ができるようになります。

スクリーショットの検知をするには、`DETECT_SCREEN_CAPTURE`の permission を定義します。DETECT_SCREEN_CAPTURE の Protection level は normal です。

```xml:AndroidManifest.xml
<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />
```

検知用の Callback は`ScreenCaptureCallback`を作成して、ライフサイクルに合わせて登録・登録解除を行います。

```kotlin:MainActivity.kt
val screenCaptureCallback = ScreenCaptureCallback {
   // Add logic to take action in your app.
}

override fun onStart() {
   super.onStart()
   registerScreenCaptureCallback(mainExecutor, screenCaptureCallback)
}

override fun onStop() {
   super.onStop()
   unregisterScreenCaptureCallback(screenCaptureCallback)
}
```

このように Android 14 ではスクリーンショットの検知が非常に簡単に実現できます。アプリ内でスクリーンショットの検知したら、安易に共有させないような注意喚起を行うケースなどで有用かと思います。
 
### 予測型「戻る」アプリ内アニメーション

バックアクションに対して、事前検知することでカスタムアニメーションなどの処理を追加することが可能になりました。`@RequiresApi(34)`で指定している Implement Members が Android 14 で追加になります。

```kotlin:MainFragment.kt
val callback = object: OnBackPressedCallback(enabled = false) {
   @RequiresApi(34)
   override fun handleOnBackStarted(backEvent: BackEvent) {
       // Create the transition
   }

   @RequiresApi(34)
   override fun handleOnBackProgressed(backEvent: BackEvent) {
       // Play the transition as the user swipes back
   }

   override fun handleOnBackPressed() {
       // Finish playing the transition when the user commits back
   }

   @RequiresApi(34)
   override fun handleOnBackCancelled() {
       // If the user cancels the back gesture, reset the state
   }
}
this.requireActivity().onBackPressedDispatcher.addCallback(callback)
```

実際にアニメーションを追加実装サンプルは[公式ページ](https://developer.android.com/about/versions/14/features/predictive-back)にあるので、実際の実装例を見てみたい方はそちらをご覧ください。

## まとめ

Android 14 の新機能概要を紹介させて頂きました。アプリによっては有用な機能もあると思うので、targetSdk 34 対応時に合わせて、アプリの新機能として検討してみてはいかがでしょうか。

## 参考

* [Features and APIs Overview](https://developer.android.com/about/versions/14/features?hl=en)
* [Android Developers Blog](https://android-developers.googleblog.com/2023/02/first-developer-preview-android14.html)