基本的には以下のドキュメントどおりなんですが、ちょっとややこしい部分があってコード読んだりしたので紹介しておきます。
OSのバージョンによらずに、Androidのシステムをアップデートできるようにしていこうという取り組みのようで、そのアップデートした部分をアプリから使えるようにしたいということのようです。
例えば、ACTION_PICK_IMAGESはAndroid 13 (API level 33で追加されたものだが、Android 11以上でR Extensions Version 2のSDK extensionsが入っている端末でも使えます。
基本
例えばACTION_PICK_IMAGESを使いたければ、ACTION_PICK_IMAGESはR Extensions 2であるとドキュメントからわかります。
Version 2以上のバージョンのExtensionをAndroid StudioのSDK Managerから入れる。
そのSDKに入っているACTION_PICK_IMAGESなどをimportするためにcompileSdkExtensionを宣言して、ビルド時に参照できるようにする。compileSdkを新しくすると新しいAndroidのAPIがコードから使えるようになるのと一緒。
android {
compileSdk = 33
compileSdkExtension = 4
...
}
以下のように判定して2以上であることを判定して、使えるとわかるので、利用する。この場合ちゃんと古いOSでも使える場合もtrueになる。
SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) >= 2
何がややこしいのか
このSdkExtensions.getExtensionVersion(Build.VERSION_CODES.R)
の引数のBuild.VERSION_CODES.Rって何。。?SdkExtensions.getExtensionVersion() >= 2ならわかるけど。。? しかもbuild.gradleでもcompileSdkExtension = 4
って宣言してたからMapみたいな感じではなくない??
SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) >= 2
(ちなみにRはAPI Level 30 Android 11に相当する)
こういうときはコードを見ましょう。
以下のブランチを見るとそのコードを見ることができます。
https://android.googlesource.com/platform/packages/modules/SdkExtensions/+/refs/heads/android13-mainline-art-release
以下に実際のSdkExtensionsのコードがあります
https://android.googlesource.com/platform/packages/modules/SdkExtensions/+/refs/heads/android13-mainline-art-release/java/android/os/ext/SdkExtensions.java
以下のように単にHashMapになっています。
R_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.r", 0);
S_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.s", 0);
T_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.t", 0);
AD_SERVICES_EXTENSION_INT =
SystemProperties.getInt("build.version.extensions.ad_services", 0);
Map<Integer, Integer> extensions = new HashMap<Integer, Integer>();
extensions.put(VERSION_CODES.R, R_EXTENSION_INT);
if (SdkLevel.isAtLeastS()) {
extensions.put(VERSION_CODES.S, S_EXTENSION_INT);
}
if (SdkLevel.isAtLeastT()) {
extensions.put(VERSION_CODES.TIRAMISU, T_EXTENSION_INT);
}
extensions.put(AD_SERVICES, AD_SERVICES_EXTENSION_INT);
ALL_EXTENSION_INTS = Collections.unmodifiableMap(extensions);
以下のようにすると端末の実際の状況が取得できます。Pixel5aで取得したところ以下のようになりました。つまり、それぞれのvalueに4が入っているという状況のようです。
adb shell getprop | grep build.version.extensions
[build.version.extensions.ad_services]: [4]
[build.version.extensions.r]: [4]
[build.version.extensions.s]: [4]
[build.version.extensions.t]: [4]
このSdkExtensions.getExtensionVersion()の引数になり得るものは以下のように定義されています。
AdServicesのAPIでは以下のようになっていて、Ad Servicesが指定されていたりします。
https://developer.android.com/design-for-safety/privacy-sandbox/reference/adservices/AdServicesState
結局の所build.gradleのcompileSdkExtension = 4とbuild.version.extensionsの数字の関係性は今の所分かっていません。おそらく今は全部1つのバージョンで管理していて、将来的にbuild.version.extensions.ad_services
やbuild.version.extensions.r
毎に別々の数字が使われたり、別々のアップデートされるようになった場合に、またGradle側のAPI変更の対応などが入るという想定なのかもしれません。 (不明。わかる方いれば教えて下さい)
アプリ側のとり得る戦略
基本的にはおそらくcompileSdkExtensionは上げてしまって(おそらくライブラリのアップデート時に設定しないとアップデートできなくなる)、あとは基本的に直接SdkExtensions.getExtensionVersion()などを使わず、JetpackなどにActivityResultContracts.PickVisualMedia.isPhotoPickerAvailable()などがあり判定できるので、それで適切に対応していくのでいいのではないかなと思います。