BillingLibraryをv1 -> v2にアップデートする

v1で作ったけど、v2が出たのでGistをアップデートした。

以下変更済みのコード。

PendingPurchaseを有効にする必要がある

enablePendingPurchases() をBuilderで叩かないといけない。絶対叩かないといけないのであれば最初からtrueにしておけばよいのかと思うが、何も考えずにUpdateされてPendingPurchaseに勝手に変更されたって言われないようにするためにやってるんだと思う。


To enable pending purchases, call enablePendingPurchases() when initializing your app. Note that if you do not call enablePendingPurchases(), you cannot instantiate the Google Play Billing Library.

消費、もしくは返答をしなければならない

onPurchasesUpdatedで渡ってきたPurchaseにはisAcknowledgedというパラメータが追加されている。渡ってきたPurchaseが問題ないことが確認できたら、消費アイテムであればconsumeするしSubscription課金であればacknowledgeを返答して課金を有効にする。isAcknowledgedを有効にするタイミングで課金されたアイテムの状態を有効にするのがよい。

developerPayloadがつけれる

消費、もしくは返答時にdeveloperPayloadをつけれる様になっている。返答時につけたdeveloperPayloadはレシートの中に含まれるようになり、以降でレシート履歴を取得したり有効なレシートを取得した際にdeveloperPayloadが取れるようになる。課金と一緒にdeveloperPayloadをつけることはできず、課金が終わってからの返答でdeveloperPayloadをつけるというのがAIDLでの課金とまだ違う。これは、GooglePlayStoreからの課金が今後有効になった場合、課金時にDeveloperPayloadをつけることができない(課金を行うのはPlayStoreアプリなので情報がない)ので、処理を共通化させるために作った仕組みなのではないかと推測している。

Responseが変更された

v1はCodeのみが返答されていたけれど、v2からBillingResultが返答されるようになった。ResponseCodeの参照位置も変わっているので変更。Responseが変更されたことで、合わせてListenerも変更された。同じ様に変えればいい

雑感

特に難しいことはない。enablePendingPurchases() を行うことと、返答されたレシートをちゃんと消費すれば難しいことはない。検証作業は本来サーバ側で行うべきなようだが、クライアント側でも検証する方法が公式から出ているので、興味がある人は見てみるといいと思う。


課金をAIDLからBilling Libraryに変更した話

以下を参考に実装した。

バージョンとコード

implementation ‘com.android.billingclient:billing:1.2’

感想

AIDLファイルを残したままにすると、デバッグビルドは通るがリリースビルドでのみこけるのに多少ハマったけど、それ以外はすごく使いやすくなっている。developerPayloadがなくなっているので使う前提のサービスだと変更はできないが、developerPayloadはdeprecatedにまではなっていないが、将来的に消えるらしいのでdeveloperPayloadに依存しないアプリを作ったほうが良い。

また、1.1から1.2へのアップデートでBillingFlowParamsのsetSku / setTypeがdeprecatedになっており、setSkuDetailsを使うことが推奨されている。引数として使用されるSkuDetailsのconstructorはjsonSkuDetailsのStringを設定する口しかなくBuilderも存在しないため自力で作ることが困難な作りになっている。GoogleとしてはquerySkuDetailsAsyncでSkuDetailsを取得したものを使用して課金処理を実行してほしいのだろう。SkuDetailsにはストアで設定したタイトルや詳細が取得できるので、これを使用してUIを作り保持しているSkuDetailsを使用してそのまま課金処理に移ってほしいという流れなのだろうと想像している。PlayStoreでの課金アイテムを変更するだけでアプリをアップデートすることなく課金アイテムを変更できるという利点もあるわけだが、タイトルにアプリ名が含まれているしストアで設定した文言がそのまま表示されるわけではないのであまり使い勝手は良くない印象を受けた。アップグレード、ダウングレードもsetOldSkuを設定するだけで対応できるのは非常にありがたい。

サービスで使用する場合はstartConnectionのエラー時に再接続処理を走らせたり、課金アイテムの消費タイミングを変更したり、課金完了時にレシート情報をサーバに送信しサーバ側で有効なレシートかチェックしたりと色々やる必要はあるが、クライアント側の処理は非常に簡潔になりすごくよい。AIDLで消費する前にBillingLibraryを使用したほうがなにかと捗りそうだ。

DataBindingでSpannableStringを使いたい

TextViewの装飾でSpannableStringを使っているのに、ObservableField()を使って値を設定すると装飾が消える。

ObservableFiled()を使って設定すれば装飾が残るのでこっちを使おう。

RealmにEnumを保存したい

RealmはEnumを保存できない。なので、nameを保存してそれを使ってenumが引ける口を作ると簡単だと思う。

WorkerManager#beginUniqueWork で複数登録する場合はDelayを入れたほうが良い

WorkerManagerのbeginUniqueWorkでWorkerを大量に突っ込むと3つ目で以下のエラーを吐いて落ちる

E/WorkerWrapper: Status for aa9f2b0d-d327-4a9f-bac1-5f3e6799603a is ENQUEUED; not doing any work

以下のようにちょっとだけDelayをいれると動いた。

うまいことWorkerをまとめる術があるのなら他にやったほうが良さそうだが、beginUniqueWorkでまとめる場合はDelayをいれたほうが無難だと思われる。

Google Sign-InでApiException: 12501がでて失敗する

以下のエラーがでて失敗する

> com.google.android.gms.common.api.ApiException 12501

手順は以下の通り

OAuth2のClientKeyが設定されてないのがだめというものを見つけたけど違った。

結局、 GoogleSignInOptions.DEFAULT_SIGN_IN を設定しないといけないところを GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN を設定していたからエラーとなっていた。

うまくいったコード

バージョンは以下

> implementation “com.google.android.gms:play-services-auth:15.0.1”

FirabseのAuthUIで[Code: 10, message: 10]のエラーが出る

FirebaseのAuthUIを試してみたけど以下のエラーがでて動かない。

com.firebase.ui.auth.FirebaseUiException: Code: 10, message: 10:

よくよくみると以下のワーニングが出ていた

Developer error: this application is misconfigured. Check your SHA1 and package name in the Firebase console.

SH256のほうがいいだろうと256しか登録していなかったが、SHA1が登録していないとだめらしい。FirebaseConsoleの設定からSHA1を登録したところ問題なく動作した。

複数のFlowableで全ての更新通知で全ての値を結合する

結論を先に書くと Flowable.combineLatest を使うと良い

flatMapで結合

こうすると、onNextが流れるたびにobserveされる物が増えていくためintProcessorで流れてきた回数strProcessorが出力されることになる。一応目的は果たして入るが、同じ値が複数流れてくるので非常に効率が悪い。

combineLatestで結合

こうすると、どちらかのonNextが流れてきたときに、他の最新データを使って結合することができる。これがやりたかったこと。

TextViewのMarqueeが動かない問題に終止符を

TextViewのMarqueeが動かない問題でいつも悩んでいるので色々なところを参考にして以下のクラスを作った。これならどの場面でも動くことは動く。

以下が参考になる。

http://saways.blogspot.jp/2011/10/textview-marquee.html
 saways.blogspot.jp 

どちらも最終的な結論は「isFocuse()でtrueを返せ!」なんだけど、isFocuse()でtrueを返すとフォーカスがTextViewにあることになるのでキーボードが開けないという困ったことが起こる。なので、StackTraceからMarqueeに必要な関数から呼ばれているかを判定してtrueを返答するようにしてるんだろうけどこれだと関数名が変わったり、別の箇所から呼ばれたりとRefactorされただけで動かなくなってしまいそうで怖い。

実際にTextViewの中身を見ると && (isFocuse() || isSelected()) で判定をしているので、どちらかを返答すればよいのだからisSelected()でtrueを返答してあげたほうが実害が少ないのでこっちを採用することで僕の環境だと上手く動いた。XMLで色々やったりしてもどうしても動かない箇所が出てくるのでこれで動かすのが今のところのベスト。もし違う実装が見つかったらまた書くかも。

rxAndroidのsubscribeOnとobserveOnの違い

よくわからなかったので動かしながら試したけど結論は

スレッドを変えたかったらobserveOnを使え!

ってことだった。

お試し1

お試し2

解説

ログを取って確認してみると以下のように動いている。

subscribeOn
Observableの動き出しのスレッドを指定。最初に指定されたものが使われる。
observeOn
以降が動くスレッドを指定:

となっている。

僕の理解ではObservable.just()の部分もmainで動くのかと思ったけど、Observable.create()からsubscribeOnで指定されたスレッドで動くっぽいように見える。subscribeOnはLibraryとか配るときにObservable.create()を動かすスレッドを固定することが出来るっていうのが利点。ライブラリ作って配布するとかしないのであれば使う必要なくてスレッドを変えたいだけならobserveOnを使わなければならない。