2020年8月10日月曜日

ACTION_GET_CONTENT で複数の MIME type を指定したいときは EXTRA_MIME_TYPES を使う

画像を ACTION_GET_CONTENT で取りたいけれど、image/* ではなく image/png と image/jpeg だけにしたいという場合は EXTRA_MIME_TYPES を指定します。このとき type には */* を指定しておきます。 val intent = Intent(Intent.ACTION_GET_CONTENT) .setType("*/*") .putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/png", "image/jpeg")) ACTION_OPEN_DOCUMENT でも使えます。


2020年8月9日日曜日

アプリ内から Google Play にレビューを投稿できるようになりました。

In-App Review API は Java/Kotlin のほか、native code と Unity からも使えます。

In-App Review API のガイドラインを確認して利用しましょう。
例えば
  • レビューのUI(ドキュメントではカードと呼んでいる)を出す前に "Do you like the app?" のような意見を聞く質問を出してはいけない。
  • カードに変更を加えたり、上や周りにオーバーレイを出したり、カードを勝手に消してはいけない。
  • 過度に出してはいけない。quotaがあり、超えるとカードが出なくなる。
などがあります。

In-App Review API を使うには Google Play Core Library を追加します。 dependencies { ... implementation "com.google.android.play:core-ktx:1.8.1" } Review Flow を開始するには、ReviewManager の requestReviewFlow() を呼び出します。requestReviewFlow() の結果が成功だと ReviewInfo が取得できます。取得した ReviewInfo を使って ReviewManager の launchReviewFlow() を呼び出します。 val manager = ReviewManagerFactory.create(context) ... private fun startReviewFlow() { manager.requestReviewFlow() .addOnCompleteListener { request -> if (request.isSuccessful) { val reviewInfo = request.result manager.launchReviewFlow(activity, reviewInfo) .addOnCompleteListener { // レビューフローが終了した // ユーザーがレビューしたのかしてないのか、 // そもそもカードが表示されたのかどうかは取得できない // アプリの通常フローを進める } } else { // なにかしら問題があったということだが、 // 結果によらずアプリの通常フローを続ける } } } In-App Review API からは、ユーザーがレビューしたのかどうか、レビューのカードが表示されたのかどうかは取得できません。


In-App Review API の挙動をテストするにはアプリが Google Play に公開されている必要がありますが、製品版として公開されている必要はなく、internal test tracks や internal app sharing に公開することでテストできます。

また、Unit Test や Instrumentation Test 用に ReviewManager の Fake が用意されています。 val manager = FakeReviewManager(context)


2020年7月16日木曜日

WorkFactory を使って WorkManager の Worker を生成する

WorkerFactory を使うので Worker のコンストラクタでは任意の引数を取れる。 class MyWorker( context: Context, params: WorkerParameters, private val api: MyApi, private val dataStore: DataStore ) : CoroutineWorker(context, params) { override suspend fun doWork(): Result { return try { val data = api.getData() dataStore.save(data) Result.success() } catch (e: Exception) { Timber.e(e) Result.failure() } } } WorkerFactory を用意する。 class MyWorkerFactory( private val api: MyApi, private val dataStore: DataStore ) : WorkerFactory() { override fun createWorker( appContext: Context, workerClassName: String, workerParameters: WorkerParameters ): ListenableWorker? { return if (workerClassName == MyWorker::class.java.name) { MyWorker(appContext, workerParameters, api, dataStore) } else { null } } } Application で Configuration.Provider を実装し、getWorkManagerConfiguration() で返す Configuration で MyWorkerFactory を指定する。
直接 MyWorkerFactory を setWorkerFactory() に渡してもいいが、DelegatingWorkerFactory を使うと複数の Factory から構成させる Factory を作ることができる。 class MyApplication : Application(), Configuration.Provider { ... override fun getWorkManagerConfiguration(): Configuration { val api = appComponent.api() val dataStore = appComponent.dataStore() val delegatingWorkerFactory = DelegatingWorkerFactory().apply { addFactory(MyWorkerFactory(api, dataStore)) } return Configuration.Builder() .setWorkerFactory(delegatingWorkerFactory) .build() } } Subcomponent を使えば WorkerFactory と Worker 両方 Dagger に生成させることもできるし、なんなら Hilt には HiltWorkerFactory が用意されている。

参考