Upgrade to Pro — share decks privately, control downloads, hide ads and more …

InAppUpdate実装&InAppBillingClientVer2.0実装例

 InAppUpdate実装&InAppBillingClientVer2.0実装例

katsuki-nakatani

July 26, 2019
Tweet

More Decks by katsuki-nakatani

Other Decks in Technology

Transcript

  1. Android関連 ・CameraX ・JetPack Compose ・Kotlin first ・Android Q ・Android Studio

    3.5 Beta1 ・PlayCoreLibrary stable ・PlayBillingLibrary 2.0 and more ...
  2. 初期化 lateinit var appUpdateManager: AppUpdateManager override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) appUpdateManager = AppUpdateManagerFactory.create(this) } MainActivity.kt
  3. リスナーの追加 lateinit var appUpdateManager: AppUpdateManager override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) appUpdateManager = AppUpdateManagerFactory.create(this) appUpdateManager.appUpdateInfo.addOnSuccessListener { task -> when (task.updateAvailability()) { } } } MainActivity.kt Why Service Bind ??
  4. ServiceBind 2019-07-05 00:12:20.248 10696-10696/org.gdgkobe.sample.inappupdate I/PlayCore: UID: [10081] PID: [10696] AppUpdateService

    : requestUpdateInfo(org.gdgkobe.sample.inappupdate) 2019-07-05 00:12:20.306 10696-10696/org.gdgkobe.sample.inappupdate I/PlayCore: UID: [10081] PID: [10696] AppUpdateListenerRegistry : registerListener 2019-07-05 00:12:20.408 10696-10730/org.gdgkobe.sample.inappupdate I/PlayCore: UID: [10081] PID: [10696] AppUpdateService : Initiate binding to the service. 2019-07-05 00:12:21.126 10696-10696/org.gdgkobe.sample.inappupdate I/PlayCore: UID: [10081] PID: [10696] AppUpdateService : ServiceConnectionImpl.onServiceConnected(ComponentInfo{com.android.vending/com.google.android.finsky.installservice.DevTriggeredU pdateService}) 2019-07-05 00:12:21.135 10696-10730/org.gdgkobe.sample.inappupdate I/PlayCore: UID: [10081] PID: [10696] AppUpdateService : linkToDeath 2019-07-05 00:12:21.255 10696-10718/org.gdgkobe.sample.inappupdate I/PlayCore: UID: [10081] PID: [10696] OnRequestInstallCallback : onRequestInfo 2019-07-05 00:12:21.257 10696-10730/org.gdgkobe.sample.inappupdate I/PlayCore: UID: [10081] PID: [10696] AppUpdateService : Unbind from service. bind PlayStoreアプリのサービスへ バインドをしている
  5. 戻り値で更新があるかどうか判断 lateinit var appUpdateManager: AppUpdateManager override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) appUpdateManager = AppUpdateManagerFactory.create(this) appUpdateManager.appUpdateInfo.addOnSuccessListener { task -> when (task.updateAvailability()) { } } } MainActivity.kt UpdateAvailability.UNKNOWN UpdateAvailability.UPDATE_AVAILABLE UpdateAvailability.UPDATE_NOT_AVAILABLE UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
  6. 更新するUIを提示するコード appUpdateManager.appUpdateInfo.addOnSuccessListener { task -> when (task.updateAvailability()) { UpdateAvailability.UPDATE_AVAILABLE ->

    { appUpdateManager.startUpdateFlowForResult( task, AppUpdateType.IMMEDIATE, // or flexible this, ImmediateActivity.UPDATE_CHECK_REQUEST_CODE ) }
  7. Immediateの場合 特に終了を意識する必要はないので、 Flowを開始すればいい。 ただし、Foregroundで処理が実行される関係上、アプリが裏側に行ってしまう場合は、 Resume処理が必要 override fun onResume() { super.onResume()

    appUpdateManager.appUpdateInfo.addOnSuccessListener { task -> when (task.updateAvailability()) { UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS -> { appUpdateManager.startUpdateFlowForResult( task, AppUpdateType.IMMEDIATE, this, ImmediateActivity.UPDATE_CHECK_REQUEST_CODE ) } } } }
  8. flexibleの場合 Flow開始後にダウンロード完了検知が必要 private fun registerInstallStateListener() { installStateUpdateListener = InstallStateUpdatedListener {

    Log.d("resumeState", "State " + it) if (it.installStatus() == InstallStatus.DOWNLOADED) { showUpdateMessage() //Snackbarを表示 appUpdateManager.unregisterListener(installStateUpdateListener) installStateUpdateListener = null } } appUpdateManager.registerListener(installStateUpdateListener) } private fun showUpdateMessage() { Snackbar.make(findViewById(R.id.root_layout), "Update Available", Snackbar.LENGTH_LONG) .setAction("Update") { _ -> appUpdateManager.completeUpdate() }.show() }
  9. Tips Immediateは強制インストールではない ☓ボタンで閉じることが出来る バックキーも有効 override fun onActivityResult(requestCode: Int, resultCode: Int,

    data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if(requestCode == UPDATE_CHECK_REQUEST_CODE && resultCode == Activity.RESULT_CANCELED){ Toast.makeText(this,"更新してくれ〜",Toast.LENGTH_LONG).show() } } onActivityResultで検知して、対応をしましょう
  10. Tips Updateするパッケージごとに Immediate,Flexibleを分けたい appUpdateManager.appUpdateInfo.addOnSuccessListener { task -> when (task.updateAvailability()) {

    UpdateAvailability.UPDATE_AVAILABLE -> { val versionCode = task.availableVersionCode() { "state": [ { "vname": "1.0.1", "vcode": 2, "type": "Flexible" }, { "vname": "1.0.2", "vcode": 3, "type": "Immediate" }, { "vname": "1.0.3", "vcode": 4, "type": "Cancel" } ] }
  11. まとめ In-App UpdateはPlay Core Libraryに含まれるAPI PlayStoreは必要 INTERNET Permissionは不要 複数トラックも見分けすることが可能 段階的リリースにも対応

    アプリケーションのアップデートを簡易的に促すことができる 公開しないと使えないので公開は必須
  12. Play Billing Library 2.0 Google Play Billing Library 1.0 リリース(2017-09-19)

    Google Play Billing Library 1.1 リリース(2018-05-07) Google Play Billing Library 1.2 リリース(2018-10-18) Google Play Billing Library 1.2.1 リリース(2019-03-04) Google Play Billing Library 2.0 リリース(2019-05-07)
  13. 初期化とConnectionの開始 class MainActivity : AppCompatActivity(), PurchasesUpdatedListener { lateinit private var

    billingClient: BillingClient ... billingClient = BillingClient.newBuilder(context).setListener(this).build() billingClient.startConnection(object : BillingClientStateListener { override fun onBillingSetupFinished(@BillingClient.BillingResponse response: Int) { when (response) { OK -> { } else -> { } } } override fun onBillingServiceDisconnected() { } })
  14. 初期化とConnectionの開始 class MainActivity : AppCompatActivity(), PurchasesUpdatedListener { lateinit private var

    billingClient: BillingClient ... billingClient = BillingClient.newBuilder(context).enablePendingPurchases().setListener(this).build() billingClient.startConnection(object : BillingClientStateListener { override fun onBillingSetupFinished(billingResult: BillingResult) { when (billingResult.responseCode) { OK -> { } else -> { } } } override fun onBillingServiceDisconnected() { } })
  15. 購入 fun startPurchase() { val params = SkuDetailsParams.newBuilder() val skuList

    = arrayListOf<String>().toMutableList().apply { add(“item001”) } params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP)  //一度、アイテムの詳細情報を取得する activity.billingClient.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList -> if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) { val flowParams = BillingFlowParams.newBuilder() .setSkuDetails(skuDetailsList.first()) .build() val responseCode = activity.billingClient.launchBillingFlow(activity, flowParams) } } }
  16. とても大事なこと 承認すること override fun onPurchasesUpdated(billingResult: BillingResult?, purchases: MutableList<Purchase>?) { when

    (billingResult?.responseCode) { OK -> { if (purchases != null) { queryPurchase() } else {} } else -> { } } } fun queryPurchase() { //購入情報を取得 val result = activity.billingClient.queryPurchases(BillingClient.SkuType.INAPP) result?.purchasesList?.firstOrNull()?.run { if(purchaseState == Purchase.PurchaseState.PURCHASED){ //承認フラグが承認されていない場合 if (!isAcknowledged) { val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder().setPurchaseToken(purchaseToken).build() activity.billingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener) } } } }