2016年1月21日木曜日

[Android] In-app Billing v3を使ってライセンス購入を実装するLicenseManagerをアップデートしました

憂鬱なムックを助ける友の会: [Android] In-app Billing v3を使ってライセンス購入を実装する で紹介していたLicenseManagerクラスですが、onCreateでnewして、onResumeでisLicensePurchased()メソッドにて購入状態を判定するという作りにしていましたが、onCreateからonResumeが走るまでにIn-app Billing APIが結果を返しきる保証は何もないので、判定を誤るという可能性がありました。

そこで、そのバグ(そう、これはバグです!すみませんっ!!)を修正しましたのでご案内します(そして、コミットログ、ミスりました。。。恥ずかしい。。)

https://github.com/shinchit/LicenseManager

使い方はほぼ変わりません。ただ、newした段階でIn-app Billing APIが結果を返し済みであることを確認してから先の処理に進むように改修していますので、newの直後にisLicensePurchased()メソッドを使っても問題ありません。

技術的には、非同期処理の順番、完了待ちを保証するために、JDeferredというライブラリを利用しています。
そのため、appレベルのbuild.gradleのdependenciesに

 compile 'org.jdeferred:jdeferred-android-aar:1.2.4'

といった記述が必要です(バージョンは適宜調整してください。動作確認をとっているのは1.2.4です)。

JDeferredの細かい使い方については、公式、または次のページがわかりやすいです。

Java - Androidの非同期処理を綺麗に書けるjdeferredが便利! - Qiita

LicenseManagerでは、以下の記述でnewの中でIn-app Billing APIへの非同期リクエストが帰ってくるのを待ち合わせています。

ちなみに、ここではJavaのλ式(ラムダ式)を利用しています。が、別に使わなくても問題ないです^^
ただ、見てお分かりの通り、λ式を使うと型の迂遠な明示が省略できるので楽に読み書きでき、スッキリして見えます。

また、GitHub上のLicenseManagerでは、アプリのライセンスキーを暗号化せずにそのまま渡していますが、実際のリリースapkを作る際にはそれはオススメできません。ライセンスキーは暗号化して保持し、これを復号化してLisenceManagerに渡すようにするべきです。とはいえ、暗号化のseedはapk内に持つため、その気になれば簡単に復号化もできてしまうのですが。。。万全を期すなら、seedを外部のサーバ(Webサーバなど)において、SSLでそれを取得してというのが良いのでしょうが、、、そこまでやる必要もないかと(というか、それをやると実装によってはオフラインでアプリを使用できないシーンが出てくるし)。
この辺の、暗号化・復号化の話はまた別エントリーで取り上げます。

[Android] Android Studioでaarライブラリを取り込む方法

Android Studioでaarで提供されているライブラリをプロジェクトに取り込むには次のようにします。

まず、プロジェクトレベルのbuild.gradleに次のようにflatDirの記述を追加します。

flatDirのdirsのパスは任意のパスを指定できますが、ここでは簡便に使えるプロジェクトのapp/libs/を指定しています。

次に、appレベルのbuild.gradleに次のように取り込むaarファイルの情報を追記します。


ここまで行ったら、プロジェクトのapp/libs/ディレクトリに目的のaarファイル(例ではHogeHoge.aar)を設置します。

あとは、Android StudioでSyncProject(下図の赤枠部)を実行すれば無事にaarがプロジェクトに取り込まれて幸せになれます。

Sync Project with Gradle Filesボタン


参考:
 AndroidStudio - Android Studio でaarをローカルから読み込む方法 - Qiita

2015年11月27日金曜日

[Capybara] [Turnip] 複数のエディションのテストで分岐がある場合の対応方法

Capybara + Turnipを使ってE2E(End-to-End)テストをする時に、テスト対象のサービスや製品に複数のエディションがあり、画面遷移は殆ど共通なのですが、あるエディションにだけに他のエディションにはない画面(遷移)がある場合のノウハウ。

まぁ、愚直にやるなら、各エディション分featureファイルを作れば良いのですが、DRYじゃないし、タイピングも怠いです。RSpecからテストを実行する時にパラメータを与えて分岐できたら幸せじゃないだろうか?とも考えたのですが、そのような方法は私は見つけられず、パラメータを覚えておかなければいけないのも考えてみると怠いです。

というわけで、featureファイルに全部テストデータを突っ込みつつ、分岐もすれば比較的怠くなくいけた感がありましたので方法を書いておきます。

まずは、サンプル。

やっていることは至ってシンプルです。Capybara + Turnipでは<>でシナリオ内に変数を埋め込むことができます(例で言うと<DDDする>が変数です)。この変数にfeatureファイルの最下部に記載した「例:」の各値を当てはめて逐次実行することをCapybara + Turnipは行ってくれます。例では、5行の定義がありますが、最初の一行はヘッダで変数名と同じにする必要があります。残り4行がデータになり、Capybara + Turnipは各行から値を取り出しては変数にその値を代入し、代入した結果と一致するstepを実行します。

例では上から順に「何もしない」、「何もしない」、「DDDする」、「何もしない」と定義していますので、1番目、2番目、4番目は<DDDする>というstepが"何もしない"になります。そして、3番目だけが、"DDDする"というstepになります。

stepsファイルを見ると、二つのstepだけ定義しています。"何もしない"と"DDDする"の二つです(もちろん略しているだけで、他にも"AAAする"、"BBBする"などが定義されていなければいけません)。step "何もしない"は見てのとおり何もしません。step "DDDする"は何か仕事をしています。

この様に、ステップ名全体を変数化し、"何もしない"といった様な何の処理もしない空のstepを用意することで、一部のエディションにだけ存在する画面に分岐してテストすることが一つのfeatureファイルで可能になります。殆どが同じ処理のシナリオをfeatureを分けるなどをして書く必要はないですし、テストデータ(例:の表組のデータのことです)もfeatureファイルに閉じ込めておくことができます(外部から入力を与える必要がない)。

ちなみに、例:の表組を使うためには、featureファイルにて「シナリオ」ではなく、「シナリオアウトライン」を使う必要があることに注意してください。

2015年11月7日土曜日

[Capybara] [RSpec] [Poltergeist] [Turnip] E2Eテストを自動化する手順

WebシステムのE2E(End-to-End)テストの自動化を、Capybara + RSpec + Poltergeist + Turnipで行ってみました。新規に自動化のスクリプトを作っていく手順を備忘で書いておきます。環境はMacOSX EI Capitanです。

Gemfileを次の内容に更新します。

そして、bundle installを実行します。
次に、RSpecとTurnipの設定ファイルを用意します。

spec/turnip_helper.rbを作ります。

ここまででテストシナリオを作成する環境の準備が整います。

では、テストシナリオを書いていきます。
Turnipで日本語が使えるようになっていますので、日本語で書いていきます。
簡単な例として、Googleにアクセスして、キーワードTurnipで検索し、その結果をsnapshotに取るというシナリオを作ってみます。

シナリオはfeatureファイルに記述します。
spec/features/google_search.featureを次の内容で作りました。

シナリオに呼応するstepファイルを記述します。ファイルの命名規則に注意してください。spec/turnip_helper.rbのDir.glob()で指定したパスにマッチするように作ります。
spec/steps/google_search_steps.rb

テストが出来上がったので実行します。実行前にスナップショットを保存するディレクトリを mkdir snapshot で作っておきます。

無事にテストが通れば次のようになります。
スナップショットも作成されています。

2015年11月4日水曜日

[Android] Recent Apps(アプリ履歴)にActivityを表示させない方法

非常にニッチなニーズなのですが、諸般の事情で実装する必要があったので備忘で書いておきます。

Recent Apps(アプリ履歴、タスク一覧ともいうのかな?)に対象のActivityを非表示にするには、対象Activityがフォアグラウンドになければ


にある通り、AndroidManifest.xmlにて対象Activityの<activity>に

android:excludeFromRecents="true"

を指定してあげれば非表示になります。

問題は、対象Activityがフォアグラウンドにある場合で、その場合は、下記のようにexcludeFromRecents以外に色々と設定してあげる必要があります。
(TargetActivityはご自身のActivity名に読み変えてください)。


正直、各設定の詳細な意味は深く理解していません(汗
おまじないに近いです。

次の記事を参考にしました。

Issue 53313 - android - Foreground service killed when receiving broadcast after acitivty swiped away in task list - Android Open Source Project - Issue Tracker - Google Project Hosting

2015/11/7追記:

もっと簡単で確実な方法がありました。onPause()でsetTheme(android.R.style.Theme_NoDisplay)すれば良いです。

protected void onPause() {
    setTheme(android.R.style.Theme_NoDisplay);
    super.onPause();
}



2015年7月20日月曜日

[MovableType] [Data API] [Perl] 画像タイプのカスタムフィールドを持つエントリーをData APIを使って投稿する


MovableTypeで画像タイプのカスタムフィールドを持つエントリー(記事)をData APIを使って投稿する方法について(しかもPerlで)

これを実現するには、
1.画像をData APIを使ってアップロードし、アイテム(Asset)に登録する
2.エントリーをData APIを使って投稿する際に、1で登録したアイテムを紐付けて登録する
という手順が必要です。

サンプルを見るのが一番わかりやすいと思いますので、次にコードを提示します。

(私的)ハマりポイントは次の通り。

1.画像をアップロードする場合にはX-MT-Authorizationでトークンを渡すだけでなく、Content-Type: multiple/form-dataも渡す必要がある。boundaryとか自力でやろうとするとこれは大変なので、HTTP::Request::CommonのPOSTメソッドを使いました。POSTメソッドにて、POST(url, Content_Type => 'form-data', ... );とすることでHTTP::Request::Commonがboundaryも含めたヘッダをよしなに処理してくれます。POSTメソッドへの引数がContent-Typeではなく、Content_Typeになっていることに要注意です。

2. 画像の実体の指定もHTTP::Request::CommonのPOSTメソッドを利用すれば、Contentパラメータ内で['ファイルのパス']とすることで処理してくれます。自力でファイルを開いたり(binmode指定して頑張ったりもして)、IO::Fileオブジェクト作って渡してみたり、色々やってみましたが、これらの方法はうまくいきません。

3.画像タイプのアセットを記事に紐付けて、編集画面にも埋め込んで表示するには、サンプルの通り、
 sprintf(qq{<form mt:asset-id='%d' class='mt-enclosure mt-enclosure-%s' style='display: inline'><a href="%s"><img src="%s"/ width="500"></a></form>}, $json->{id}, $json->{class}, $json->{url}, $json->{url})
というように、formタグの中にimgやaタグを組み込んだ値を指定します。style='display: inline;'というように';'を含んだ値を渡すとJSONモジュールがエラー吐いて死にますので、外します。formの他の属性はおまじないだと思ってください。

参考:
 MovableType Data API v2
 HTTP::Request::Common
 実装メモ:Movable Type 6の便利なData APIを利用して画像を出力する : アークウェブのブログ
 

2015年7月19日日曜日

[ImageMagick] 縦長画像のみ90度回転して表示される問題を解決する

昨今のデジカメやiPhoneなどで撮影した画像をWebにアップすると縦長画像が90度回転して表示されてしまう問題に遭遇しました。画像のExif情報が悪さしている模様です。

全てが縦長の画像であれば対処は単純なのですが、横長と混在している状態だとどうしたらいいの?ってなります。が、ご安心。ImageMagickに素敵なオプションがありました。
auto-orientです。次のように使うと、縦長の画像だけ判定して90度回転してくれます。