2020年6月24日水曜日

Android 11 での android:allowBackup の挙動変更

Android 11 で android:allowBackup の挙動が少し変わります。


behavior-changes-11#device-to-device-file-transfer

If your app targets Android 11, you can no longer disable device-to-device migration of your app's files using the allowBackup attribute. The system automatically allows this functionality.
However, you can still disable cloud-based backup and restore of your app's files by setting the allowBackup attribute to false, even if your app targets Android 11.


Android 11 をターゲットにしている(targetSdkVersion = 30+)アプリでは、android:allowBackup="false" にしても device-to-device migration を無効にすることはできません。ただし、android:allowBackup="false" にしていれば cloud-based backup and restore は無効にできます。

device-to-device migration とは何かというと、Google Pixel などの local device-to-device transfer をサポートしているデバイスに別のデバイスからケーブル経由でバックアップデータを転送することです。


Backup 関係のリソース

2020年6月22日月曜日

Android アプリの詳細設定画面を開く

Settings.ACTION_APPLICATION_DETAILS_SETTINGS を使います。

  1. startActivity(  
  2.     Intent(  
  3.         Settings.ACTION_APPLICATION_DETAILS_SETTINGS,  
  4.         Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)  
  5.         // または  
  6.         // Uri.parse("package:${BuildConfig.APPLICATION_ID}")  
  7.     )  
  8. )  





adb から試すなら
  1. $ adb shell  
  2. $ am start -a android.settings.APPLICATION_DETAILS_SETTINGS -d package:[packageName]  


2020年6月20日土曜日

Kotlin メモ : orEmpty()

?: "" をしてくれる String?.orEmpty() という拡張関数が用意されています。
  1. val text: String? = ...  
  2. val nonNullText: String = text.orEmpty()  



2020年6月18日木曜日

Google Play の subscriptions policy の変更に対応するための参考リソース

2020/4/16 に Google Play の subscriptions policy が変更され、提供する subscription について透明性の高い情報を提供することが求められるようになりました。

subscriptions policy の変更についてのブログ
Android Developers Blog | Building user trust through more transparent subscriptions

例えば、価格や課金の頻度をちゃんと表示しましょうね、無料期間のあとは課金されることをちゃんと表示しましょうね、ということなのですが、とはいえ何がOKで何がNGなのか、何をすればいいのかこのブログではよくわからないので、関係するリソースを集めました。


チェックリスト
Subscriptions checklist

このチェックリストの最後のほうにある Learn more でいける
Academy for App Success | Set up subscriptions
の方がわかりやすいチェックリストがあります。ただし日本語だとチェックリストが出ないので、Profile で Preferred language を English にする必要があります。


↓ OK/NG の例があります(少しだけ)。
Developer Policy Center | Monetization and Ads | Subscriptions


OK/NG の例はこの動画が一番わかりやすいと思います。




おすすめは ↑ の動画を見た後に Academy for App Success | Set up subscriptions の英語版をやる、です。


2020年6月16日火曜日

WorkManager の CoroutineWorker, Worker をテストする

こういう CoroutineWorker があるとします。
  1. class SendHelloWorker(  
  2.     private val context: Context,  
  3.     params: WorkerParameters  
  4. ) : CoroutineWorker(context, params) {  
  5.   
  6.     override suspend fun doWork(): Result {  
  7.         val name = inputData.getString("name") ?: return Result.failure()  
  8.   
  9.         val api = (context.applicationContext as MyApplication)  
  10.             .appComponent  
  11.             .api()  
  12.   
  13.         return try {  
  14.             api.sendHello("Hello $name")  
  15.             Result.success()  
  16.         } catch (e: Exception) {  
  17.             Result.failure()  
  18.         }  
  19.     }  
  20. }  
まず CoroutineWorker や Worker をテストするには worker-testing artifact を使います。
  1. testImplementation "androidx.work:work-testing:$work_version"  
CoroutineWorker のテストには TestListenableWorkerBuilder を使います。
TestListenableWorkerBuilder は version 2.1.0 から追加されています。

TestListenableWorkerBuilder の build() で Worker のインスタンスを取得し、doWork() を呼んで結果をチェックします。
  1. class SendHelloWorkerTest {  
  2.   
  3.     private lateinit var api: MyApi  
  4.     private lateinit var context: Context  
  5.   
  6.     @Before  
  7.     fun setup() {  
  8.         api = mock()  
  9.   
  10.         val appComponent: AppComponent = mock()  
  11.         whenever(appComponent.api()).thenReturn(api)  
  12.   
  13.         val application = MyApplication()  
  14.         application.setAppComponent(appComponent)  
  15.   
  16.         context = mock()  
  17.         whenever(context.applicationContext).thenReturn(application)  
  18.     }  
  19.   
  20.     @Test  
  21.     fun doWork() {  
  22.         val worker = TestListenableWorkerBuilder<SendHelloWorker>(  
  23.             context,  
  24.             inputData = Data.Builder()  
  25.                 .putString("name""Android")  
  26.                 .build()  
  27.         )  
  28.             .build()  
  29.   
  30.         runBlocking {  
  31.             val result = worker.doWork()  
  32.   
  33.             assertThat(result).isEqualTo(ListenableWorker.Result.success())  
  34.             verify(api).sendHello("Hello Android")  
  35.         }  
  36.     }  
  37.   
  38.     @Test  
  39.     fun doWork_fail() {  
  40.         val worker = TestListenableWorkerBuilder<SendHelloWorker>(  
  41.             context,  
  42.             inputData = Data.Builder()  
  43.                 .putString("name""Android")  
  44.                 .build()  
  45.         )  
  46.             .build()  
  47.   
  48.         whenever(api.sendHello("Hello Android")).thenThrow(RuntimeException())  
  49.   
  50.         runBlocking {  
  51.             val result = worker.doWork()  
  52.   
  53.             assertThat(result).isEqualTo(ListenableWorker.Result.failure())  
  54.             verify(api).sendHello("Hello Android")  
  55.         }  
  56.     }  
  57. }  


CoroutineWorker ではなく Worker をテストするときは TestListenableWorkerBuilder ではなく TestWorkerBuilder を使います。



参考 : https://developer.android.com/topic/libraries/architecture/workmanager/how-to/testing-worker-impl



2020年6月10日水曜日

Kotlin : Uri から ByteArray を取得する (Uri to ByteArray)

Kotlin の readBytes() 拡張関数を使うとすっきり。
  1. val context: Context = ...  
  2. val uri: Uri = ...  
  3.   
  4. val byteArray: ByteArray? = context.contentResolver  
  5.     .openInputStream(uri)  
  6.     ?.use { it.readBytes() }  



2020年6月2日火曜日

androidx.test.ext:truth を使ったときに IllegalAccessError が出たらバージョンを 1.3.0 以降にする

androidx.test.ext.truth にある IntentSubject などを使うとき
  1. com.google.truth:truth:0.42  
  2. androidx.test.ext:truth:1.2.0  
だと動くのですが、Truth のバージョンを以下のように 1.0.1 にすると
  1. com.google.truth:truth:1.0.1  
  2. androidx.test.ext:truth:1.2.0  
java.lang.IllegalAccessError: tried to access method com.google.common.truth.Subject.actual()Ljava/lang/Object; from class androidx.test.ext.truth.content.IntentSubject

というエラーが出ます。

IntentSubject 内で Subject の actual() メソッドにアクセスしているのですが、これが 0.42 のときは protected だったのが package private に変わってアクセスできなくなったのが原因です。

そのため、この新しい Truth に対応した androidx.test.ext:truth のバージョンを使えば OK です。
  1. com.google.truth:truth:1.0.1  
  2. androidx.test.ext:truth:1.3.0-rc01