Quantcast
Channel: Yukiの枝折
Viewing all 146 articles
Browse latest View live

Android:Service.dumpでサービスの状態をダンプする

$
0
0
Serviceにはdumpメソッドが用意されています。
これは下記コマンドが実行された時に、サービスの状態をダンプするメソッドです。
$ adb shell dumpsys activity service <yourservicename>
Service.dump
http://developer.android.com/reference/android/app/Service.html#dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[])

dumpメソッドにはPrintWriterインスタンスが引数として渡されます。
これにサービスの情報を入力すれば、ログとしてそれが出力されます。

・動作サンプル
$ adb shell dumpsys activity service yuki.test.messenger/.MyMessenger
MyMessenger.java
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] options) {
    pw.println("MyMessenger dump!!!");
}
-出力結果-
SERVICE yuki.test.messenger/.MyMessenger 41fa2ef0 pid=6151
  Client:
    MyMessenger dump!

Service.dumpメソッドを提供することで、開発者はサービスの情報を好きなタイミングで取得することが可能です。
※ただし、対象のサービスが実行中であることが前提です
ActivityManagerServiceもこのメソッドを提供しています。
ActivityManagerServiceをdumpすればデバッグ時に有益な情報を確認できます。
http://yuki312.blogspot.jp/2012/04/androidactivity.html

Object.toStringのそれと同じく、Service.dumpは非常に有益なメソッドです。
toStringをオーバライドする際の一般契約と同じように、dumpする内容は"簡素であり、読みやすく、有益な表現"であるべきでしょう。


●サンプルコード

Service.dumpの第三引数 options にはコマンド引数が格納されます。
$ adb shell dumpsys activity service yuki.test.messenger/.MyMessenger -a -b -c
であれば
 options[0] = "-a"
 options[1] = "-b"
 options[2] = "-c"
といった具合です。

これらを活用すれば、必要に応じたダンプ情報を得ることができます。
また、よくある"ヘルプコマンド"も実装可能です。

下記はそのサンプルコードです。
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] options) {
    boolean dumpAll = false;
    boolean dumpClient = false;
    boolean dumpMessage = false;
    boolean dumpState = false;

    int optionIndex = 0;
    while (optionIndex < options.length) {
        String option = options[optionIndex];
        if (TextUtils.isEmpty(option) || option.charAt(0) != '-') {
            break;
        }
        optionIndex++;

        if ("-a".equals(option) || "-all".equals(option)) {
            dumpAll = true;
        } else if ("-c".equals(option) || "-client".equals(option)) {
            dumpClient = true;
        } else if ("-m".equals(option) || "-message".equals(option)) {
            dumpMessage = true;
        } else if ("-s".equals(option) || "-state".equals(option)) {
            dumpState = true;
        }  else if ("-h".equals(option) || "-help".equals(option)) {
            pw.println("MyMessenger dump options:");
            pw.println("  [-a] [-c] [-m] [-s]");
            pw.println("    a[ll]: dump all info.");
            pw.println("    c[lient]: reply target.");
            pw.println("    m[essage]: handle message log.");
            pw.println("    s[tate]: service state change log.");
            return;
        } else {
            pw.println("Unkown argument: " + option + "; use -h for help.");
        }
    }

    pw.println("Service state");
    pw.println("    Service create time is " + new Date(mOnCreateTime));
    pw.println("    " + mCurrentState);
    pw.println();

    if (dumpClient || dumpAll) {
        pw.println("Client(" + mClients.size() + ")");
        for (ClientRecord cr : mClients.keySet()) {
            pw.println("    " + cr);
        }
        pw.println();
    }

    if (dumpMessage || dumpAll) {
        pw.println("Message Logs");
        for (MessageLog log: mMessageLogs) {
            pw.println("    " + log);
        }
        pw.println();
    }

    if (dumpState || dumpAll) {
        pw.println("ServiceState Logs");
        for (ServiceState.ServiceStateLog log: mCurrentState.getStateLogs()) {
            pw.println("    " + log);
        }
        pw.println();
    }
}
-出力結果-
・引数無し
$ adb shell dumpsys activity service yuki.test.messenger/.MyMessenger
SERVICE yuki.test.messenger/.MyMessenger 41fd0950 pid=6151
  Client:
    Service state
        Service create time is Sun Jan 06 03:58:13 GMT+00:00 1980
        Current service state is RUNNING
・引数指定(-c:クライアント情報出力, -m:メッセージ履歴出力)
$ adb shell dumpsys activity service yuki.test.messenger/.MyMessenger -c -m
SERVICE yuki.test.messenger/.MyMessenger 41fa2ef0 pid=6151
  Client:
    Service state
        Service create time is Sun Jan 06 03:58:28 GMT+00:00 1980
        Current service state is BLOCKED

    Client(2)
        ClientRecord [ PackageName=test.pkg1, ID=1, hash=b6f4c7b1 ]
        ClientRecord [ PackageName=test.pkg2, ID=1, hash=b6f4c7b2 ]

    Message Logs
        03:58:44.570: { what=3 when=-1ms }
        03:58:44.339: { what=2 when=-1ms }
        03:58:28.681: { what=1 when=-5ms }
・引数指定(-a:全情報出力)

$ adb shell dumpsys activity service yuki.test.messenger/.MyMessenger -a
SERVICE yuki.test.messenger/.MyMessenger 41fa2ef0 pid=6151
  Client:
    Service state
        Service create time is Sun Jan 06 03:58:28 GMT+00:00 1980
        Current service state is BLOCKED

    Client(2)
        ClientRecord [ PackageName=test.pkg1, ID=1, hash=b6f4c7b1 ]
        ClientRecord [ PackageName=test.pkg2, ID=1, hash=b6f4c7b2 ]

    Message Logs
        03:58:44.570: { what=3 when=-1ms }
        03:58:44.339: { what=2 when=-1ms }
        03:58:28.681: { what=1 when=-5ms }

    ServiceState Logs
        03:58:44.571: { RUNNING => BLOCKED }
        03:58:44.340: { NEW => RUNNING }
・引数指定(-h:ヘルプ)

$ adb shell dumpsys activity service yuki.test.messenger/.MyMessenger -h
SERVICE yuki.test.messenger/.MyMessenger 41fa2ef0 pid=6151
  Client:
    MyMessenger dump options:
      [-a] [-c] [-m] [-s]
        a[ll]: dump all info.
        c[lient]: reply target.
        m[essage]: handle message log.
        s[tate]: service state change log.
・引数指定(-p:サポートしない引数)

$ adb shell dumpsys activity service yuki.test.messenger/.MyMessenger -p
SERVICE yuki.test.messenger/.MyMessenger 41fa2ef0 pid=6151
  Client:
    Unkown argument: -p; use -h for help.
    Service state
        Service create time is Sun Jan 06 03:58:28 GMT+00:00 1980
        Current service state is BLOCKED

便利ですね。好きなタイミングで実行できるのが強力です。

注意すべき点として、、、
ログ出力の場合と同じく、dump情報で出力する情報には注意が必要です。
# Service.dumpで出力される情報はbugreportにも出力されます。
開発時以外に必要ないのであれば、ビルドタイプでdump情報を絞る等の対応を考えます。

以上です。

Android:toLowerCase/toUpperCaseに注意

$
0
0
最近のLintは賢くなって、String.toLowerCaseやtoUpperCaseをLocale引数無しで使用すると警告を出すようになりました。
Locale指定がないと、予期せぬ事態を招く可能性があるからです。
本稿はLocaleについて少し触れてみたいと思います。

突然ですが、下記の評価式はtrue or falseどちらを返すでしょうか。
"YUKI".toLowerCase().equals("yuki")
答えは true 、といきたいところですがfalseにもなり得るのです。

評価結果がfalseとなるのは、たとえばデバイスの言語設定がトルコ語であった場合です。


トルコ語では"I"の小文字は"i"ではありません。
そのため、"YUKI"を小文字化しても"yuki"にはならないためfalseと評価されます。
つまり、Localeを指定しないString型の文字列比較では、言語設定(Locale)によって評価結果が変化する可能性があるということです。
これを回避するには、ロケールに依存しないようにtoLowerCase(Locale.ENGLISH)を使用します。

String.toLowerCase(Locale)リファレンス
http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#toLowerCase(java.util.Locale)


それでは、下記の評価式はtrue or falseどちらを返すでしょうか。
"YUKI".equalsIgnoreCase("yuki")
答えは、ロケールに関係なく true が返されます。

先述のtoLowerCaseの件を考えると、equalsIgnoreCase(String)もロケールに依存するように思えます。
しかし、equalsIgnoreCaseの評価はロケールに依存しないのです。

これは、equalsIgnoreCaseで用いられるのは、String.toLowerCase(), String.toUpperCase()ではなく、
Character.toLowerCase(char), Character.toUpperCase(char)であるためです。

Characterクラスが持つそれぞれのメソッドはロケールに依存したケースマッピングをサポートしていません。
そのため、言語設定がトルコ語になっても評価に影響することがないのです。

String.equalsIgnoreCase(String)リファレンス
http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#equalsIgnoreCase(java.lang.String)


i18nの敷居の高さが伺える事例でした。
ただ、下記のような状況があり得るというのには多少違和感を感じますね。
 "YUKI".toLowerCase().equals("yuki")=false
 "YUKI".equalsIgnoreCase("yuki")=true

その他参考:
Local:
http://developer.android.com/reference/java/util/Locale.html#default_locale

以上です。

Android:Notification領域のmoreアイコン

$
0
0
Notificationアイコンが通知領域に収まらない時、オーバフローを表現するために"more icon"が表示されます。

しかし、Android標準の不具合によりこの機能はうまく動作しません。
#各キャリア端末ではこの不具合は改修されて再現しませんが、Nexus7等の標準機であれば確認できます。

エミュレータで動作確認する際に"more icon"が表示されない場合はこの不具合である可能性があります。
不具合のfixソースはandroid.gitへ既にコミットされています。

commitcd231432ff16cb35aa08cd7b9ca801d26bef261f
"[+&gt;" more icon was never shown in PhoneStatusBar

"[+&gt;" more icon was never show in status bar because
the member variable for this icon was not initialized
from resources. This fix enables "[+&gt;" icon to appear
in status bar when the number of indications in status
bar becomes large.
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -415,6 +415,7 @@
     mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
     mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
     mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
+    mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
     mNotificationIcons.setOverflowIndicator(mMoreIcon);
     mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
     mTickerView = mStatusBarView.findViewById(R.id.ticker);
from android.git

以上です。

Android:商用署名(キーストア)をデバッグ用に変更する手順

$
0
0
チームでAndroidアプリ開発をする上で
 「複数人で開発する際に、デバッグキーを共有したい
という方は多いでしょう。

また、セキュリティ上危険ではありますが、
 「商用署名を一時的にデバッグキー(デバッグ署名)として運用したい
なんてこともあるかもしれません。

これらを、Eclipse/ADTのデバッグ機構で実現するには少しコツが入ります。

本稿では、Eclipse/ADTで商用署名(キーストア)をデバッグキーとして運用するために必要な
  商用署名(キーストア)をデバッグ用に変更する手順
についてまとめました。

一般的に、商用署名は厳重に管理すべきものであり、無闇に複製/編集すべきものではありません。
商用署名を直接編集するなら、破損しても復旧できるようにバックアップは必ず取りましょう。
本稿で使用するパスワードはテスト用です。本番環境ではセキュアなパスワードを指定してください。

今回の検証環境
---
 OS: WindowsXP
 Eclipse: 3.7.1(Indigo)
 ADT: 21.1
---

検証で使用するキーストア情報。
---
 キーストアファイル名
   yuki.keystore
 キーストアパスワード
   yukipass
 エイリアス
   yukialias
 エイリアスパスワード
   yukialiaspass
---

●Eclipseにデバッグキーを設定する方法

下記の項目から設定します。
[Window]>[Preferences]>[Android]>[Build]

図の①に、使用するデバッグ用キーストアファイルのパスを指定します。
ここに予め作成しておいたyuki.keystoreを指定すると...
次のエラーで弾かれてしまいます。

エラー:Keystore was tampered with, or password was incorrect


「キーストアが改ざん、あるいはパスワードが正しくない」旨のエラーです。
Eclipse/ADTで使用できるデバッグキー(キーストア)情報は下記の条件を満たす必要があります。
---
 キーストアファイル名
   任意
 キーストアパスワード
   android
 エイリアス
   androiddebugkey
 エイリアスパスワード
   android
---

つまり、デバッグキーとして使用するキーストアパスワードは
  android
である必要があるのです。
yuki.keystoreのキーストアパスワードは現在"yukipass"であるため、デバッグキーとして使用できません。

それでは、yuki.keystoreをデバッグキーとして使用するためにキーストア情報を変更します。


●キーストアのパスワードを変更する方法

$keytool -storepasswd -keystore yuki.keystore
キーストアのパスワードを入力してください: <現在のキーストアパスワード>
新規 keystore password: <新しいキーストアパスワード> # ★ android を指定
新規 keystore password を再入力してください: <新しいキーストアパスワードを再入力>
Windowsなら下記を実行(JDK_HOMEのパスは自環境用に読み替えて下さい)
C:\Program Files\Java\jdk1.6.0_12\bin>keytool.exe -storepasswd -keystore yuki.keystore
このキーストアをEclipseに設定してもダメです。
次のエラーで弾かれます。

エラー:Unable to find debug key in keystore!

「キーストア内にデバッグキーが見つからない」旨のエラーです。
デバッグキーとして使用するエイリアスは
  androiddebugkey
である必要があります。


●エイリアスを変更する方法

$keytool -changealias -alias yukialias -keystore yuki.keystore
destination の別名を入力してください: <新しいエイリアス> # ★ androiddebugkey を指定
キーストアのパスワードを入力してください: <現在のキーストアパスワードを入力> # 既に android に変更済なら android で
<yukialias> の鍵パスワードを入力してください。 <現在のエイリアスパスワードを入力>
Windowsなら下記を実行。
C:\Program Files\Java\jdk1.6.0_12\bin>keytool.exe -changealias -alias yukialias -keystore yuki.keystore
このキーストアをEclipseに設定してもまだダメです。
↓のエラーで弾かれます。

エラー:Cannot recover key

「キーを復元できない」旨のエラーです。
デバッグキーとして使用するエイリアスパスワードもやはり
  android
である必要があります。


●エイリアスパスワードを変更する方法

keytool -keypasswd -alias androiddebugkey -keystore yuki.keystore
キーストアのパスワードを入力してください:
<androiddebugkey> の鍵パスワードを入力してください。 <現在のエイリアスパスワード>
新規 <androiddebugkey> の鍵のパスワード: <新しいエイリアスパスワード>  # ★ android を指定
新規 <androiddebugkey> の鍵のパスワード を再入力してください:<新しいエイリアスパスワードを再入力>
Windowsなら下記を実行。
C:\Program Files\Java\jdk1.6.0_12\bin>keytool.exe -keypasswd -alias androiddebugkey -keystore yuki.keystore
このファイルをEclipseに指定すれば見事デバッグキーとして使用できます。


●おわりに

本稿では、わざと数回に分けてキーストア情報を変更しましたが、keytoolのオプションを工夫すればコマンドをまとめることができます。
keytoolコマンドに限りませんが、(テスト目的を除き)安全でないシステムで実行する場合は、コマンドやスクリプトにパスワード情報を含めてはいけません。コマンドやスクリプトはヒストリとして蓄積され、パスワードが漏洩する可能性があります。
オプションで必要なパスワードを指定しなかった場合は、パスワードの入力を求められるのでそこで入力するようにします。

参考:
keytool - 鍵と証明書の管理ツール
Android Developers - アプリケーションへの署名

以上です。

Android:AlermManagerとネットワークアクセスを組み合わせる際のマナー

$
0
0

AlermManagerを使用すれば、任意の時間に処理を実行することができます。
Android Developers : AlermManager.set (int, long, PendingIntent)

時間指定の方法には次のタイプがあります。
  • 絶対時間で指定するRTCタイプ
  • 相対時間で指定するELAPSED_REALTIMEタイプ
○○時○○分に処理したい場合はRTC、今から○○分後に処理したい場合はELAPSED_REALTIMEといった具合です。

どちらのタイプを使用するかはアプリ要件に左右されますが、もしあなたのアプリケーションが
 タイマー発火を契機にネットワークアクセスする仕様、かつRTCタイプを採用しようとしている
のであれば一度次のことを検討してみましょう。
この処理/仕様は、相対時間指定(○○分後)で実現可能か。あるいはそのように処理/仕様を変更できないか。
もし、この問いに"YES"であるならRTCタイプではなくELAPSED_REALTIMEタイプを採用するのがよい選択です。

これには次の理由があります。
絶対時間指定の場合、アプリからのネットワークアクセスが集中する
数百万(あるいは数千万!)DLされたアプリが、特定の時間に一斉にネットワークアクセスを開始すればどうなるかは容易に想像できますね。

ネットワークアクセスのタイミングをRTCタイプでしか実現できないのであれば、
「端末によって絶対時間指定の時刻をずらせないか」
を検討するのも良い案です。
ネットワークアクセス時刻を設定/初期化する際に、分散の大きい乱数でも加えてあげればネットワークアクセスも分散されます。

重要なのは"ネットワークアクセスを集中させないこと"です。
ELAPSED_REALTIMEタイプを採用したからといって"AM8:00"までの時間差分を指定しては問題の解決にはなりません。

この手の話題は"アプリのマナー"に関わる部分です。
マナーを守るためにはソースコードの修正が必要かもしれません。
もしかすると、あなたのアプリのパフォーマンスがほんのちょっぴり低下するかもしれません。
適用したからといって、ユーザレビューの評価点にはそれほど影響しないでしょう。
しかし、こういったマナーを守るアプリが増えれば、きっとその反対よりは良い世の中になりそうです。
プロとして"気の利いたアプリ"を作っていきたいものですね。

以上です。

Android:ActionBar下部にProgressBarを表示

$
0
0

上図のようなActionBarの直下にプログレスバーを表示するUIがあります。
ActionBarの機能かと思ったのですが、どうもそうではなさそうなので自作しました。
# 類似のPJにPullToRefreshがあります

まずはActionBar下部に表示される影を消します。
これがあると、ActionBarとプログレスバーの間に隙間ができてしまいます。
<activity
    android:name="yuki312.android.actionbarprogress.MainActivity"
    android:label="@string/app_name"
    android:windowContentOverlay="@null" >

次にプログレスバーを配置するためのレイアウトを定義。
画面最上部にプログレスバーを配置します。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="0dp" >
    <ProgressBar
        android:id="@+id/view_actionbar_progress"
        android:layout_width="match_parent"
        android:layout_height="3dp"
        android:indeterminateDrawable="@drawable/ic_progress_indeterminate" />
</RelativeLayout>

indeterminateDrawableにプログレスバーの画像を指定します。
プログレスバーの画像はアニメーションさせるのが一般的です。
1コマ毎の画像を用意してアニメーションリソースを用意しても良いのですが、
リソース節約のため、ここではShapeで描画します。
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false" >
    <item android:duration="50">
        <shape android:shape="rectangle" >
            <gradient
                android:angle="180"
                android:centerY="1.0"
                android:endColor="@color/progress_dark"
                android:centerColor="@color/progress_light"
                android:startColor="@color/progress_dark"
                android:type="linear" />
        </shape>
    </item>
    <item android:duration="50">
        <shape android:shape="rectangle" >
            <gradient
                android:angle="180"
                android:centerY="0.8"
                android:endColor="@color/progress_dark"
                android:centerColor="@color/progress_light"
                android:startColor="@color/progress_dark"
                android:type="linear" />
        </shape>
    </item>
    <item android:duration="50">
        <shape android:shape="rectangle" >
            <gradient
                android:angle="180"
                android:centerY="0.6"
                android:endColor="@color/progress_dark"
                android:centerColor="@color/progress_light"
                android:startColor="@color/progress_dark"
                android:type="linear" />
        </shape>
    </item>
    <item android:duration="50">
        <shape android:shape="rectangle" >
            <gradient
                android:angle="180"
                android:centerY="0.4"
                android:endColor="@color/progress_dark"
                android:centerColor="@color/progress_light"
                android:startColor="@color/progress_dark"
                android:type="linear" />
        </shape>
    </item>
    <item android:duration="50">
        <shape android:shape="rectangle" >
            <gradient
                android:angle="180"
                android:centerY="0.2"
                android:endColor="@color/progress_dark"
                android:centerColor="@color/progress_light"
                android:startColor="@color/progress_dark"
                android:type="linear" />
        </shape>
    </item>
    <item android:duration="70">
        <shape android:shape="rectangle" >
            <gradient
                android:angle="180"
                android:centerY="0.0"
                android:endColor="@color/progress_dark"
                android:centerColor="@color/progress_normal"
                android:startColor="@color/progress_dark"
                android:type="linear" />
        </shape>
    </item>
   
    <item android:duration="100">
        <shape android:shape="rectangle" >
            <gradient
                android:angle="180"
                android:centerY="0.2"
                android:endColor="@color/progress_dark"
                android:centerColor="@color/progress_normal"
                android:startColor="@color/progress_dark"
                android:type="linear" />
        </shape>
    </item>
    <item android:duration="100">
        <shape android:shape="rectangle" >
            <gradient
                android:angle="180"
                android:centerY="0.4"
                android:endColor="@color/progress_dark"
                android:centerColor="@color/progress_normal"
                android:startColor="@color/progress_dark"
                android:type="linear" />
        </shape>
    </item>
    <item android:duration="100">
        <shape android:shape="rectangle" >
            <gradient
                android:angle="180"
                android:centerY="0.6"
                android:endColor="@color/progress_dark"
                android:centerColor="@color/progress_normal"
                android:startColor="@color/progress_dark"
                android:type="linear" />
        </shape>
    </item>
    <item android:duration="100">
        <shape android:shape="rectangle" >
            <gradient
                android:angle="180"
                android:centerY="0.8"
                android:endColor="@color/progress_dark"
                android:centerColor="@color/progress_normal"
                android:startColor="@color/progress_dark"
                android:type="linear" />
        </shape>
    </item>
    <item android:duration="100">
        <shape android:shape="rectangle" >
            <gradient
                android:angle="180"
                android:centerY="1.0"
                android:endColor="@color/progress_dark"
                android:centerColor="@color/progress_normal"
                android:startColor="@color/progress_dark"
                android:type="linear" />
        </shape>
    </item>

</animation-list>
これで、ActionBar下部にプログレスバーが表示されます。

ソース+apkをgithubにアップしています。
https://github.com/YukiMatsumura/ActionBarProgress

以上です。

Android:SAFの複数ファイル選択モードと要ログインサービスのために

$
0
0
Android4.4で導入されたStorageAccessFramework(SAF)について。
SAFが導入されたことでファイルブラウズ機能の提供が非常に手軽になりました。

SAFでは複数ファイル選択モードが具備されています。
当該モードを使用するにはIntent Extraとして下記を設定します。

intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, flag);
SAFの選択UIでファイル名を長タップすることで、複数選択モードに移行します。

複数選択モードのonActivityResultについて。
単一選択モードではIntent の data に選択ファイルのURIが格納されますが、
複数選択モードではIntent の ClipData にURIが格納されています。

ClipData clip = data.getClipData();

Uri[] contentUri = new Uri[clip.getItemCount()];
for (int i = 0; i < clip.getItemCount(); i++) {
    contentUri[i] = clip.getItemAt(i).getUri();
}

話は変わって。

SAFのセキュリティについて。
SAFは対象のファイルがオンラインストレージ上にあるのか、ローカルストレージ上に
あるのかも隠蔽しようとします。
サービスによってはオンラインストレージ上のファイルを閲覧するのにログインを求めるケースがあります。
これをSAF上で実現するためには、DocumentProvider.queryRootを実装する際に
ユーザが非ログイン状態の場合は空のCursorを返すことで実現できます。

public Cursor queryRoots(String[] projection) throws FileNotFoundException {
...
    if (!isServiceProvide()) {
        return emptyCursor;
}
サービス提供の状態に変化(非ログイン状態からログイン状態へ移行等)があった場合、
getContentResolver.notifyChangeによる変更通知により、SAFの情報を更新します。

setviceStateChanged();
getContentResolver().notifyChange(DocumentsContract
        .buildRootsUri(AUTHORITY), null);
以上です。

Android:FileProviderでファイル共有

$
0
0
他アプリとファイル共有したい場合下記の方法があります。
  • StorageAccessFramework
  • ContentProvider
  • MODE_WORLD_READABLE/WRITABLE (非推奨)

もし、アプリ連携の間だけファイルを共有したい場合は
一時許可ContentProviderのイディオムに従ったFileProviderが便利です。

FileProviderはContentProviderを継承したクラスです。
AndroidManifestに定義する際、一時許可ContentProviderのための設定と、
共有ディレクトリを指定します。
<provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.example.myapp.fileprovider"
        android:grantUriPermissions="true"
        android:exported="false">
    <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/filepaths" />
</provider>
コンテンツへのアクセス権限を付与するために、grantUriPermissionsをtrueに。
また、プロバイダ自身は非公開(exported=false)に設定します。

共有ディレクトリの指定はmeta-dataとして定義します。
共有ディレクトリリストのフォーマットが下記。
共有”ファイル”ではなく”ディレクトリ”であることに注意が必要です。
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="my_images" path="images/"/>
    ...
</paths>
files-path
 共有コンテンツがアプリ内領域のコンテンツである場合の要素として指定します。
 Context.getFilesDir()で取得できるfiles/に共有コンテンツがある場合に使用します。

files-path/@name
 この値は共有コンテンツURIのパスセグメントとして利用されます。
 実パスと一致している必要はなく、アプリ内部のファイル構造を隠すことができます。

files-path/@path
 パスにはfiles/のサブディレクトリ名を指定します。
 特定のファイル名ではなくサブディレクトリであることに注意が必要です。

例えば、下記の定義である場合、
<files-path name=“image" path=“share_image" />
共有コンテンツのURIは
 content://com.example.myapp.fileprovider/image/hoge.jpg
実際に共有されるコンテンツは
 com.example.myapp/files/share_image/hoge.jpg

共有コンテンツがアプリ内領域ではなく、Context.getExternalFilesDir()で取得できる
外部領域にある場合はexternal-pathを要素名に定義します。
<external-path name="name" path="path" />
あるいは、Context.getCacheDir()で取得できるキャッシュ領域にある場合は
cache-pathを要素名として定義します。
<cache-path name="name" path="path" />
複数の共有ディレクトリを定義することも可能です。
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name=“share_images" path="images/"/>
    <cache-path name=“share_docs" path="docs/"/>
</paths>

使ってみる

FileProviderはContentProviderのように独自クラスとして拡張する必要がなく、
そのまま使用できるのが便利です。
アプリは前述の共有ディレクトリの情報さえ指定すれば準備完了です。

コンテンツ共有の方法は基本的に一時許可コンテンツプロバイダと同じですが、
FileProviderの場合は共有するコンテンツのURIを生成しておく必要があります。
File shareImgPath = new File(Context.getFilesDir(), “images");
File shareImg = new File(shareImgPath, “share.jpg");
Uri contentUri = FileProvider.getUriForFile(getContext(), "com.example.myapp.fileprovider", shareImg);
// content://com.example.myapp.fileprovider/share_images/share.jpg
FileProvider.getUriForFileが共有コンテンツURIを作成してくれます。
あとは、 Intent.setFlags()にFLAG_GRANT_READ_URI_PERMISSION,
FLAG_GRANT_WRITE_URI_PERMISSIONを指定してアプリ連携すれば、
アクセスを一時的なものに限定したコンテンツの共有が実現できます。
Uri contentUri = FileProvider.getUriForFile(this, "com.example.myapp.fileprovider", shareImg);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, getContentResolver().getType(contentUri));
intent.addFlags(
    Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(intent, 0/*request code*/);
FileProviderには、次のメソッドが備えられています。

FileProvider.query()
 共有コンテンツのファイル名とファイルサイズを格納したCursorを返します。
 カラム名には下記が指定されています。
  OpenableColumns.DISPLAY_NAME:ファイル名
  OpenableColumns.SIZE:ファイルサイズ

FileProvider.delete()
 指定の共有コンテンツを削除します。

FileProvider.openFileDescriptor()
 ファイルディスクリプタを取得できます。

updateやinsertのメソッドはデフォルトでサポートされないため、
UnsupportedOperationExceptionがスローされます。

参考リンク:

以上です。

Android+GradleなCI環境構築

$
0
0
ゴール:
 AWS EC2上にApache + Jenkinsの環境を構築。
 AndroidStudioで作成したプロジェクトをGithubへpush。
 GithubのWebhookでJenkinsにpush通知し、Gradleを使ってビルドさせます。

確認環境:
 クライアント:
  PC Mac OS X 10.9.2
  Gradle ver. 1.12
  Android Studio ver. 0.5.8
  Android Build tools gradle ver. 0.10.1
 サーバ:
  Amazon Linux AMI release 2014.03
  Apache/2.2.27(Unix) built:Apr 25 2014 22:26:04
  Jenkins ver.1.563-1.1
  JDK ver.1.7.0_55
  git ver. 1.8.3.1
  Gradle ver. 1.12
  Android SDK Build tools ver.19.1.0
  Android Build tools gradle ver. 0.10.1

AWS EC2インスタンス準備までは下記のサイトを参考に進めました。

参考:EC2にJenkinsによるCI環境を作成する | Developers.IO
http://dev.classmethod.jp/cloud/aws/jenkins_on_ec2/

EC2インスタンスタイプ:m1.small(試作程度なら micro でもOK)
EBSのボリュームサイズですが、初期の8GiBだとAndroidSDKのフルインストールが
ディスクフルとなって入りきらなかったので80GiBに増量して使用しています。

参考:ボリュームのストレージ領域を拡張する | AWS Documentation.
http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ebs-expand-volume.html

EC2インスタンスにユーザ"ec2-user”でSSH接続
$ ssh -i ./jenkins-key.pem ec2-user@54.xxx.xxx.xxx

●前準備

EC2 AmazonLinuxのタイムゾーンは、米国時間が初期設定なので日本時間に設定します。
$ sudo cp -P /usr/share/zoneinfo/Japan /etc/localtime
$ date

●JDKのインストール

ビルド環境構築のため、まずはJDKを導入。
AmazonLinuxは初期状態でJRE1.7.0_55が入っているようです(2014.5.19現在)
$java -version
java version "1.7.0_55"
OpenJDK Runtime Environment (amzn-2.4.7.1.40.amzn1-x86_64 u55-b13)
OpenJDK 64-Bit Server VM (build 24.51-b03, mixed mode)

JDKはrpmでインストールします。直接rpmファイルを取得するのが難しいので、
まず、Mac(ローカル)にサーバへインストールしたいJDKの.rpmをダウンロード。

DL先:Java SE Development Kit 7 Downloads | Oracle
http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html
 => jdk-7u55-linux-x64.rpm

rpmファイルをscpでサーバに転送(最後のコロンを忘れずに)
$ scp -i ~/Documents/dev/jenkins-key.pem jdk-7u55-linux-x64.rpm ec2-user@54.xxx.xxx.xxx:

サーバのec2-userホームに.rpmファイルが転送されるのでこれをインストール。
$ sudo rpm -ivh jdk-7u55-linux-x64.rpm

インストールが完了したらJDK1.7.0_55に切り替え。
$ sudo alternatives --install /usr/bin/java java /usr/java/jdk1.7.0_55/bin/java 2000

切り替わったことを確認します。
$java -version
java version "1.7.0_55"
Java(TM) SE Runtime Environment (build 1.7.0_55-b13)
Java HotSpot(TM) 64-Bit Server VM (build 24.55-b03, mixed mode)

あとは環境変数を設定して、JDKの準備は完了。
$ export JAVA_HOME=/usr/java/jdk1.7.0_55/

●Apache+Jenkinsをインストール

とりあえずApacheをインストール。
$ sudo yum install httpd

次にJenkinsをインストール。
↓のコマンドを打ちたいところだけれど、これだとエラーがでました。
$ sudo yum install jenkins
> パッケージ jenkins は利用できません。

そのため、yumリポジトリにJenkinsを追加してからインストール。
$ wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
$ rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key
$ sudo yum install jenkins

参考:RedHat Linux RPM packages for Jenkins | Jenkins CI
http://pkg.jenkins-ci.org/redhat/

インストールが終わったらJenkinsの設定を確認します。
$ sudo vim /etc/sysconfig/jenkins
Jenkinsのポート番号が8080であることを確認。
JENKINS_PORT=“8080"

念のため、8080ポートが使用済みでないか確認。
8080が未使用であれば問題ないので次へ(使用中なら別のポート番号を割り当ててください)
$ netstat -an | grep LISTEN
$ lsof -i -n -P

この時点で一度httpd, jenkinsサービスを起動して、アクセス確認してみます。
$ sudo service https start
$ sudo service jenkins start

うまくサービス開始できたら下記のURIにアクセス。
(Jenkinsのサービス起動には1分程かかる場合があります)
http://54.xxx.xxx.xxx:8080



今のURIでは不便なので、Jenkinsのドキュメントルートを変更します。
Jenkins設定ファイルのJENKINS_ARGSにドキュメントルートパスを指定します。
$ sudo vim /etc/sysconfig/jenkins
JENKINS_ARGS="--prefix=/jenkins”

これでJenkinsに下記のURIでアクセスできるようになります。
http://hogehoge:8080/jenkins

双方のサービスは自動起動するように設定しておきましょう。
$ sudo chkconfig httpd on
$ sudo chkconfig jenkins on

設定が反映されているか確認します。
$ /sbin/chkconfig --list | grep -e 'jenkins' -e 'httpd'

●Jenkinsユーザの設定

再度下記サイトを参考に、Jenkinsの初期設定(セキュリティ設定)を行います。
参考:EC2にJenkinsによるCI環境を作成する | Developers.IO
http://dev.classmethod.jp/cloud/aws/jenkins_on_ec2/

Jenkinsのホームディレクトリはパッケージ管理ソフトでインストールした場合、
/var/lib/jenkins になります。

●AndroidSDKのダウンロード

AndroidSDKをダウンロードします。
ダウンロードサイト:http://developer.android.com/sdk/index.html

次の手順でダウンロードリンクをコピー(2014.05.19現在ではr22.6.2が最新)
DOWNLOAD FOR OTHER PLATFORMS > SDK Tools Only > Linux32 > 64-bit

/usr/localにAndroidSDKを展開。
$ cd /usr/local/
$ sudo wget http://dl.google.com/android/android-sdk_r22.6.2-linux.tgz
$ sudo tar -xvzof android-sdk_r22.6.2-linux.tgz

ビルドに必要なツールを落とします。
$ sudo ./android update sdk --no-ui

↑のコマンドだと、エミュレータイメージ等もまとめてダウンロードするのでサイズが大きくなります。
$ sudo android update sdk -t platform -u
などとすれば、ダウンロードしたいモジュールを絞ることができます。
(今回は面倒だったのですべてダウンロードしました)

ダウンロードが完了したら環境変数を設定。
$ export ANDROID_HOME=/usr/local/android-sdk-linux/

JenkinsにもANDROID_HOMEのパスを追加します。
Jenkins > Jenkinsの管理 > システムの管理 > グローバル・プロパティ
 キー:ANDROID_HOME
 値:/usr/local/android-sdk-linux/

念のためAndroidSDKのビルドツールバージョンを確認しておきましょう。
$ ls $ANDROID_HOME/build-tools/

今回は19.1.0を使用しています。
Gradleでビルドする際のBuildToolsバージョンとあわせる必要があるので覚えておきます。

●Github連携(GitHub設定)

Githubへのpushをトリガに、JenkinsのワークスペースへgitリポジトリをCloneします。
GitHubにはpushをトリガに特定URIを叩く機構(Webhook)が用意されています。
Webhookを使って、「GitHubにpushされたらJenkinsでビルド」を実現します。

GitHubとの連携には鍵認証が必要です。
Jenkinsからのアクセスとなるので、Jenkins用のsshキーを作成。
$ cd /var/lib/jenkins
$ sudo mkdir .ssh
$ sudo chown jenkins .ssh
$ sudo -u jenkins -H ssh-keygen -t rsa -C jenkins@54.xxx.xxx.xxx
$ sudo chmod 755 ../.ssh

次に、公開鍵(id_rsa.pub)の内容をGitHubのSSH Keyとして登録。
GitHub > AccountSetting > SSH Keys > Add SSH Key
Titleは適当に。Keyは公開鍵の内容をペースト。

GitHubに鍵の登録ができたら接続テストをします。
$ sudo -u jenkins ssh -T git@github.com

問答の結果、次のメッセージが表示されれば成功。
Hi YukiMatsumura! You've successfully authenticated, but GitHub does not provide shell access.

これでGitHub連携の準備は完了です。

●GitHub連携(Jenkins設定)

JenkinsがWebhookできるように設定します。

まず、AmazonLinuxにgitをインストール。
$ sudo yum install git-core

次はJenkinsの設定。
Jenkins > Jenkinsの管理 > プラグインの管理 > [利用可能]タブ から
"GitHub Plugin”をインストールし、Jenkinsを再起動。

再起動が終わったら、Webhookを受信するJenkinsジョブを準備します。
"新規ジョブ作成”からジョブを作成します。
ジョブの種類は"フリースタイル・プロジェクトのビルド”とし、適当なジョブ名をつけて作成。

ジョブの設定項目は下記を参照。
GitHub project:GitHubリポジトリのURI(ブラウザで確認する際のURIでOK)
ソースコード管理:Gitを選択
 Repository URI:先ほどGitHub projectで入力したURIのhttps:スキームを"git:"に変更。
ビルド・トリガ:"Build when a change is pushed to GitHub"を選択

●GitHub連携(GitHub Webhook設定)

ブラウザからWebhook対象のGitHubリポジトリに移動し、
Settings > Webhooks & Services > Servicesの[Add service] > [Jenkins GitHub plugin]
Jenkins hook urlにはJenkinsサーバのルートに/github-webhook/を追加した文字列を指定します。
http://54.xxx.xxx.xxx:8080/jenkins/github-webhook/

設定後、GitHub側でWebhookURI編集画面にある[Test service]を押下すれば接続テストができます。
対象のJenkinsジョブにある"GitHub Hook Log”にアクセスログが残っていれば疎通確認OKです。

●AndroidStudioのプロジェクトをGitHubにpush

AndroidStudioとGitHubの連携については割愛します。
今回は無用な問題を避けるためにAndroidプロジェクトの
build.gradleに指定する各ビルドツールのバージョンを明示的に指定します。
・プロジェクトルートのbuild.gradle
buildscript {
    dependencies {
        classpath 'com.android.tools.build:gradle:0.10.1'

・モジュール毎のbuild.gradle
android {
    buildToolsVersion "19.1.0"
com.android.tools.build:gradleのバージョンが変わるとビルドが通らないことがよくあります。
buildToolsVersionについてはAmazonLinuxに展開したAndroidSDK側にも同じver.のものが必要です。

プロジェクトをGitHubにPushしたらJenkinsのジョブを確認。
新しいビルドがスケジューリングされているのがわかります。
ワークスペースにはGitHubにあるファイルがCloneされます。

●ビルド

Cloneしたソースをビルドします。
先ほど作成したジョブの”設定”からジョブを再編集します。

Gradle Wrapperを使ってビルドするために下記を設定します。
ビルド > [ビルド手順の追加 > “シェルの実行”
 シェルスクリプト:./gradlew build

これで、GitHubからリポジトリCloneした後にGradleによるビルドが実行されます。
今の状態でビルドしてみましょう。
# GitHubからClone済みであればジョブの"ビルド実行”からビルド開始できます

ビルド成功ならばOK。ビルド失敗ならば原因解析します。

ジョブ画面のビルド履歴からビルド結果の詳細が確認できます。
ビルド失敗した場合はビルド結果画面にある”コンソール出力”で失敗理由を確認します。

・失敗例1
What went wrong:
A problem occurred configuring project ':app'.
> SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.

環境変数”ANDROID_HOME”がjenkinsに設定されていません。
先述のグローバル・プロパティにANDROID_HOMEを設定することで解決します。

・失敗例2
* What went wrong:
Execution failed for task ':app:mergeDebugResources'.
> /var/lib/jenkins/jobs/hogehoge/workspace/build/exploded-aar/com.android.support/appcompat-v7/19.1.0/res/drawable-xxhdpi/abc_ic_voice_search.png: Error: Cannot run program "/usr/local/android-sdk-linux/build-tools/19.1.0/aapt": error=2, No such file or directory

aaptは32bitアプリケーションなので、32bit用ライブラリをインストールする必要があります。
まずは標準Cライブラリ(glibc)をインストール。
$ yum install glibc.i686

次に共有ライブラリの依存関係を調べます。
$ ldd /usr/local/android-sdk-linux/build-tools/19.1.0/aapt

手元の環境では下記の結果が得られたので、"not found”のライブラリをインストールしていきます。
linux-gate.so.1 =>  (0xf772a000)
librt.so.1 => /lib/librt.so.1 (0xf771b000)
libdl.so.2 => /lib/libdl.so.2 (0xf7716000)
libpthread.so.0 => /lib/libpthread.so.0 (0xf76fc000)
libz.so.1 => not found
libstdc++.so.6 => not found
libm.so.6 => /lib/libm.so.6 (0xf75de000)
libgcc_s.so.1 => not found
libc.so.6 => /lib/libc.so.6 (0xf7421000)
/lib/ld-linux.so.2 (0xf772b000)

libz.so.1のパッケージを検索、インストール。
$ yum whatprovies libz.so.1
 zlib-1.2.7-10.17.amzn1.i686 : The compression and decompression library
 リポジトリー        : amzn-main
 一致          :
 Provides    : libz.so.1
$ yum install zlib-1.2.7-10.17.amzn1.i686

libstdc++.soも同じく。
$ yum whatprovides libstdc++.so.6
 libstdc++44-4.4.6-4.81.amzn1.i686 : GNU Standard C++ Library
 リポジトリー        : amzn-main
 一致          :
 Provides    : libstdc++.so.6
$ yum install libstdc++44-4.4.6-4.81.amzn1.i686

libgcc_s.so.1も同じく。
$ yum whatprovides libgcc_s.so.1
 libgcc44-4.4.6-4.81.amzn1.i686 : GCC version 4.4 shared support library
 リポジトリー        : amzn-main
 一致          :
 Provides    : libgcc_s.so.1
$ yum install libgcc44-4.4.6-4.81.amzn1.i686

・その他失敗例
ビルドに失敗する要因は様々ですが、開発環境とJenkinsとで異なるGradleバージョンを使用している
ことが原因である可能性もあります。

・おわりに
今回の内容にはセキュリティの観点が含まれていません。
実運用する際にはAWS, GitHub, Jenkinsのセキュリティ設定にご注意ください。

以上。

Android:AppEngineBackend with GCMメモ

$
0
0

Google Cloud Messaging for Android(GCM)を利用するためのメモ。

メモ:

  • 最大4kbのペイロード・データのメッセージを送出可能
  • Android2.2以上かつGooglePlayStoreがインストールされた端末が必要
  • Android3.0以前の端末ではGoogleアカウントがセットアップされている必要がある
  • GCMはGoogle Play serviceに組み込まれた(com.google.android.gms.gcm)
  • 従来のAPI群は非推奨に(com.google.android.gcm)
  • GoogleCloudMessaging(API)の利用にはGoogle Play services version 3.1以上が必要
  • メッセージ送信時に対象デバイスがオフラインであってもGCMはこれをキューイング/ストア>再送する機能を持つ
  • 3rdPartyサーバはexponential backoffの方式を組み込み、GCMサーバと通信すること
  • GCMメッセージはアプリ固有のPermission付きBroadcastIntent経由で通知される
  • GCMメッセージを受信すると対象アプリが起動されるため、メッセージ監視のためにアプリ起動し続ける必要はない
  • メッセージ処理はWakeLockを取得してService上で実行するが吉
  • GCMメッセージを受信したくない場合はunregisterも可能
  • Genymotionを使用する場合のBackendサーバアドレスは10.0.3.2

GCM基本フロー

  1. アプリケーションはGCMを有効化するための登録を行う
  2. 3rdPartyサーバはデバイス向けメッセージをGCMに送信
  3. アプリケーションはGCMからメッセージを受信する

準備するもの

  1. Google API Projectの作成Google Developers Console
  2. Project Numberを控える(GCM sender IDとして使用.)
  3. API \& auth.からGoogle Cloud Messaging for Androidを有効化.
  4. GCMのServer Keyを作成しAPI Key取得(3rdPartyサーバとGCMサーバ間認証で使用)参考:Obtaining an API Key

3rdPartyサーバの作成

GCMサーバ(GCM connection server)とやりとりするサーバを作成する必要があります。
Implementing GCM Server

App Engine Backend with GCM

試験用途であればApp Engine Backend with Google Cloud Messagingで手軽にGCMバックエンドを作成できます。
利用するには新規ModuleとしてBackendモジュールを作成します(AndroidStudio0.8.2以降)。

Backendモジュールの作成から実行までの詳細手順は下記を参考。
“App Engine Backend with Google Cloud Messaging” Template

Backendモジュール導入に必要な作業は次の通りです。

  1. 取得済みGCMのAPI Keyを登録
  2. build.gradleの編集
  3. Backendサーバへのデバイス登録処理の実装

GCM API Keyを登録

Backendモジュールを作成したらGCMのAPI Keyを登録します。
API Keyは<backend>/src/main/webapp/WEB-INF/appengine-web.xmlに定義されているので、
これを取得済みのものに置き換えます。

<property name="gcm.api.key" value="YOUR_KEY_HERE"/>

YOUR_KEY_HEREを取得済みのAPI Keyに置換。

<property name="gcm.api.key" value="AIza..."/>

これでAPI Keyの登録は完了です。

build.gradleの編集

(この作業はAndroidStudio v0.8.2より後のバージョンでは不要になるかもしれません)
AndroidStudio v0.8.2でBackendモジュールとメインとなるappモジュールを関連づけすると、
ビルドで問題(preDexLibraryタスクでOutOfMemoryやIllegalArgumentException)が発生します。
これを回避するためにappからappengine-endpoints(-deps)ライブラリのコンパイル指定をコメントアウトします。

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:19.0.+'
compile 'com.google.android.gms:play-services:5.0.77'
// appengine-endpoints(-deps)はBackendモジュールに含まれているため、appモジュールでの個別指定は不要
// compile 'com.google.appengine:appengine-endpoints:1.9.1'
// compile 'com.google.appengine:appengine-endpoints-deps:1.9.1'
compile project(path: ':gcmbackend', configuration: 'android-endpoints')
}

Backendサーバへのデバイス登録処理の実装

下記のGcmRegistrationAsyncTaskクラスを取り込みます。
2.2. Registering devices with Google Cloud Messaging backend

GcmRegistrationAsyncTaskに定義されているSENDER_IDを自分のもの(GCM APIのProject Number)に置換します。
また、BackendサーバのURIを必要に応じて変更します。

  • Android Emulatorを使用している場合:http://10.0.2.2:8080/_ah/api/(変更なし)
  • Genymotionを使用している場合:http://10.0.3.2:8080/_ah/api/

Android Emulatorにおける10.0.2.2はホストループバックインターフェイスのエイリアスです(localhost)。
Genymotionでは10.0.3.2がこれにあたります。

全ての作業が終わったらBackendモジュールを実行し、ローカルサーバを起動します.
localhost:8080でGCMを送信するフォームが表示されます。
適当なActivityからGcmRegistrationAsyncTaskのexecuteを実行するとBackendサーバに端末のRegistrationIDが登録されます。
RegistrationID登録後にGCM送信フォームからテキストを送信すると端末にPush通知が届きます。

Androidアプリケーションの作成

GCMクライアントとなるAndroidアプリケーションを作成する場合はGoogleCloudMessaging APIの使用が推奨されています。

Google Play Serviceのセットアップ

アプリケーションの作成にはGoogle Play Serviceを使用します。
アプリケーションはGoogle Play ServiceのResourceにアクセスするため、単純に.jarを参照するだけでは動作しません。
AndroidStudioの場合はbuild.gradleのdependencyセクションに次を追記します。

dependencies {
compile "com.google.android.gms:play-services:3.1.+"
}

GCMを使用するにはGoogle Play Service3.1以上である必要があります。

AndroidManifest.xmlの編集

次のPermissionを追加します。

  • com.google.android.c2dm.permission.RECEIVE: GCMサービスへの登録とメッセージ受信に必要
  • android.permission.INTERNET: 3rdPartyサーバへのRegistrationIDの登録に必要
  • android.permission.WAKE_LOCK: (optional)メッセージ受信時のWakeLock取得に必要
  • YourApplicationPackage + ".permission.C2D_MESSAGE": 他アプリからGCMへ登録されることやメッセージを横取りされるのを防ぐのに必要
  • com.google.android.c2dm.intent.RECEIVE: categoryに自アプリのパッケージ名を指定する。GCMメッセージ受信に必要
  • android.permission.GET_ACCOUNTS: GCMに必要なGoogleアカウント情報取得に必要(Android4.0.4以前で必要)
    a

    <!-- AndroidManifest.xml -->
    <manifest package="com.example">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <!-- Android4.0.4以前をサポートする場合に必要 -->
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <!-- YourApplicationPackage + ".permission.C2D_MESSAGE" -->
    <permission android:name="com.example.permission.C2D_MESSAGE"
    android:protectionLevel="signature" />
    <uses-permission android:name="com.example.permission.C2D_MESSAGE" />

GCMの有効化

アプリケーションが初めてGCMを利用する前にGoogleCloudMessaging(API)のregister()メソッドを使ってGCMサービスへ登録する必要があります。
このメソッドはRegistrationIDを返します。参考:GoogleCloudMessaging

GCMへサービス登録する前にGoogle Play Serviceの状態をチェックします。
Google Play Serviceが古いバージョンである場合、Google Play Serviceの更新をユーザに促します。
アプリ起動中にGoogle Play Serviceがダウングレードされるケース等を考慮して、バージョンチェックはonResume()で行います。

@Override
protected void onResume() {
super.onResume();
try {
PlayServiceUtil.updatePlayServiceIfNeeded(this, new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
MyActivity.this.finish();
}
});
} catch (PlayServiceUtil.PlayServiceException e) {
// Play Serviceが使用できない致命的状態
Log.e(TAG, "Play Service is not available.");
this.finish();
}
}

public static void updatePlayServiceIfNeeded(Activity activity, DialogInterface.OnCancelListener cancelListener) throws PlayServiceException {
final int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(activity);
switch (status) {
case ConnectionResult.SUCCESS:
return;
case ConnectionResult.SERVICE_DISABLED:
case ConnectionResult.SERVICE_INVALID:
case ConnectionResult.SERVICE_MISSING:
case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(status, activity, 0);
if (cancelListener != null) {
dialog.setOnCancelListener(cancelListener);
}
dialog.show();
return;
default:
throw new PlayServiceException("Play Service is not available. status=" + status);
}
}

メッセージ送信

3rdPartyサーバ(以降”サーバ”)がメッセージ送信する時のシーケンス

  1. サーバはGCMにメッセージを送信
  2. GCMサーバは送信対象のデバイスがオフラインであればメッセージをキューイング/ストアする
  3. GCMサーバは送信対象のデバイスがオンラインになればメッセージを送信する
  4. デバイスはメッセージを受信すると配送すべきアプリケーションに向けてメッセージをブロードキャストする。
    配送されるのはアプリ固有のPermissionが設定されたBroadcastIntent。
  5. メッセージ処理が些末なものでない場合はPowerManager.WakeLockを取得してService上で実行する

メッセージ受信

デバイスがGCMメッセージを受信した時のシーケンス

  1. システムは、受信したメッセージからKey/Valueペアを展開
  2. システムは、com.google.android.c2dm.intent.RECEIVEのIntentとextraにKey/Valueデータを載せてアプリケーションに連携
  3. アプリケーションはIntentからKey/Valueを展開してデータを受け取る

参考サイト:
1. Google Cloud Messaging -android developers-

Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.

AndroidTV: Get Started

$
0
0

AndroidTVについてメモ.
*本稿はAndroid L Developer Previewの情報がベースであり,正式版では変更されている可能性に注意

開発環境: AndroidStudio v0.8.3

TV Design

  • TVデバイス向けに効果的で魅力的なUIを構築するために,リビングルームで何がうまく機能するのかを理解すること.
  • TV向けアプリケーションはリモコンを使って簡単に操作できるシンプルなUIを提供する必要がある.
  • ユーザはTVデバイスからおよそ10フィート(およそ3メートル)離れた場所から操作する.
  • TVは友人や家族等,大勢と画面を共有するデバイスである.
  • ユーザのアクティビティはコンテンツの内容によって変化する.
  • レコメンドはユーザの嗜好を表現する.
  • TVデバイスのサイズと解像度は大きくなる一方,ユーザがTVに求めるエクスペリエンスは”シンプルで整頓されていること”である.
  • AndroidTVでのサウンド効果は映画のような相互作用をもたらす.
  • TVデバイスでのエクスペリエンスの良し悪しは,画面上にある要素の数,間隔,サイズに左右される.
  • TVデザインは常に横向きで使用される.
  • LCDやプラズマTVではしばしばスムーシングとシャープ化のフィルタをかけられ,カラーレンダリングはPCモニターでの見た目とマッチしない.
  • スクリーンのサイズは他のAndroidデバイスよりもはるかに大きいものの,色やディテールはそれに劣る.
  • TVアプリケーションはTheme.Leanbackのテーマ適用が推奨される.
  • UIが欠けないようOverscanの影響を考慮する.レイアウトの周囲余白として10%のマージンを設けるべき.
  • シンプルなsans-serifフォントを使用し,アンチエイリアシングを適用する.
  • タッチスクリーン,カメラ,GPSは多くのデバイスに搭載されるがTVには搭載されていない.

Design for TV

Home

ホームスクリーンはTVの始点であり,検索,レコメンドコンテンツ,アプリケーション一覧,セッティングを提供する場でもある.

TVプラットフォームにはGoogleの検索エンジンを活かした検索機能が搭載されている.
異なるコンテンツ間のコネクションを築き,お気に入りの映画から新たなアーティストを発見したり,
旅行を計画する際にはYoutube上のコンテンツや写真に出会えるかもしれない.
アプリケーション内検索をさせるためのヒント

Recommendations

レコメンドはユーザのメディア消費活動を促進するため,ユーザに関連するコンテンツを動的に表示する.
レコメンド一覧はパーソナライズされ,他のデバイスで活動再開する手段も提供する.
ユーザに有意義なコンテンツをオススメするなどホームスクリーンの中心的な機能となる.

レコメンドはユーザの最近の行動や,繰り返しアクションをベースとするだけでなく,ユーザの嗜好を表現する.
システムまたはアプリケーションからのアクションやNotification情報も表示される.
TV向けアプリケーションはコンテンツに注目させるためにレコメンド一覧に情報を提供することもできる.
Making Recommendations

Application And Game

アプリケーションとGameの一覧はホームスクリーンの中でも特別な領域になる.
アプリケーション,Gamesそれぞれの領域はユーザの使用状況を反映するように表示される.

Settings

ホームスクリーンの下部にはAndroidとTVデバイスに関する設定にアクセスできる.

Creative Vision for TV

Casual Consumption

TVはエンターテインメントインターフェイスであり,コンピュータやモバイルデバイスとは異なる.
ユーザのアクティビティはコンテンツの内容によって変化する.
映画を見ているときはリラックスして,Gameの時は夢中になり,リビングで友人といる時は置き物になる.

ユーザがTVを点ける時は,コンテンツにすぐにでもアクセスしたいと考えている.
コンテンツには1クリックまたは2クリック程で手軽にアクセスさせるように.

Cinematic Experience

夢中にさせるようなユーザ体験を目指すこと.
少しのUIで多くのコンテンツを表示する.視覚映像,動き,音を使ってユーザを満足させる.
情報を伝えるために長々としたテキストは使わず,音と映像で表現すること.

Simplicity

AndroidTVはシンプルで魅力的なものである.それは抵抗なくコンテンツやアプリケーションを探して楽しめることに関係する.
少ないナビゲーションでアクションできることが理想である.
できる限り少ない画面数でアプリケーションにエントリさせ,コンテンツに夢中になれるよう工夫すること.
できる限りユーザにテキスト入力はさせない.テキスト入力させたい場合はボイスインプットの使用を考える.

Pattern for TV

TV向けアプリケーションに関するいくつかのパターン.

一般的にTVデバイスの操作にはD-PAD(Directional pad)を使用する.
このタイプのコントローラは上下左右の移動に制限されている.
アプリケーションをデザインする際はリストやグリッドUIで,上下左右の移動経路が明確になっているかを確認すること.

D-PADによる操作が基本となることで,当然UI部品はフォーカスを持つ.
どの部品がフォーカスを受けるのか一目でわかるように,フォーカスさせるために複雑な操作を必要としないように心がけること.
スケール,光と影,透明度,アニメーションやそれらの組み合わせでフォーカスを表現する.

Icon

TVデバイス上で実行されるアプリケーションはシステムUIで表示するために次のアイコン画像を追加する必要がある.

アプリケーションバナーはホームスクリーンで表示され,アプリケーションを起動するランチャーアイコンとして使用される.
下記はバナーアイコンの要件

  • 320 x 180px, xhdpi resource.
  • 画像にテキストを含めるべき.もし複数の言語をサポートするなら各言語毎のリソースを用意する.

Recommendation Icons

レコメンドアイコンは,レコメンドカードにある有色背景の上に小さなアイコンで表示される.
下記はレコメンドアイコンの要件.

  • モノカラー:サイズは16 x 16dp. 白色(#fff)で背景透過の.png形式
  • アイコンはセンタリングされていること

アプリケーションアイコンはいくつかのカードで低彩度化,レイヤー表示されることがある.

Background Image

背景画像はアプリケーションの裏で表示される. 背景画像は視覚的に好奇心,情報,ブランディングを提供する.
Leanback support libraryに含まれるBrowseFragmentとDetailsFragmentクラスはフォーカスを失った際に背景画像を更新する等のサポートを持つ.
下記は背景画像への要件

  • フルカラーで1920 x 1080px

背景画像が要件に適合しない場合はスクリーンにフィットするようにスケールされる.

Audio Feedback

AndroidTVでのサウンド効果は映画のような相互作用をもたらす.
よく熟考し,ユーザアクションや,積極的にインタラクションしないユーザに対してフィードバックサウンドを追加する.
(e.g., 他のことに気が散っているユーザやマルチタスクの場合など)
それ以外では,視覚的メッセージの代替えとして,たとえばリストの終端に達した場合や領域外に移動しようとした場合等が該当する.

Layout

TVデバイスでのエクスペリエンスの良し悪しは,画面上にある要素の数,間隔,サイズに左右される.
TVデバイスのサイズと解像度は大きくなる一方,ユーザがTVに求めるエクスペリエンスは”シンプルで整頓されていること”である.

現状,巨大化する解像度や画面領域は,より多くの情報を表示するためではなく,より良い品質で情報を表示するために使用される.
例えば,レイアウトを大きく見せてコンテンツを美しく見せたり,読みやすいように文字を大きくしたり,間隔を設ける.

もしコンテンツの閲覧や再生するアプリケーションを作成する場合,Leanback support libraryにあるFragmentを使用できる.
これらのレイアウトはTVデバイス向けに特化されたレイアウトを持っている.

TV向けアプリケーションのための機能デザインと魅力的なレイアウトを作成するためのポイント.

  • 横向きレイアウトデザインであること. TVデザインは常に横向きで使用される.
  • 挿絵に使うassetsはHD解像度(1920 x 1080px)向けにデザインすること.
  • ナビゲーションコントロールはスクリーンの左か右側に配置する. 上下はコンテンツ領域として確保する.
  • セクションで区切られたUIを作成するのにFragmentを活用する.そしてListViewよりGridViewの方がより水平領域を使える.
  • レイアウトの間に間隔を設けて雑然としたインタフェースを避ける.

Overscan

OverscanはTVテクノロジ発展の過程で,TVコンテンツが表示される安全地帯より外側の領域を指す用語.
今でもフラットなHDTVスクリーンのいくつかは外側の領域が表示されない.

TVスクリーンのデザインには,表示されない可能性のあるOverscan領域のことを考えて10%の余白を設けること.
1920 x 1080pxのスクリーンでは,この余白幅は画面端上下から27px,画面端左右から48pxになる.

Color

TVでのカラーレンダリングはPCモニターやモバイルのそれと比べて不正確である.
LCDやプラズマTVではしばしばスムーシングとシャープ化のフィルタをかけられ,カラーレンダリングはPCモニターでの見た目とマッチしない.

この微妙な色合いや明るさの違いが,要素間の違いを打ち消したり必要以上の強調を生む.
彩度の高い色(赤,緑,青)のためにも,広域に白が使われることは避けた方が良い.
TVのコントラスト設定により,非常に暗い色やマッドな色は区別がつかなくなる恐れがあるため使用を避けるべき.

Typography

TVアプリケーションUIでのテキストはTVを見ている距離からでも確認できるようにしなければならない.
最低要件としてフォントサイズは12sp.デフォルトのフォントサイズ設定は18spであるべき.
下記はTVアプリケーションとして推奨されるガイドライン.

  • Card Title: Roboto Condensed 16sp
  • Card Subtext: Roboto Condensed 12sp
  • Browse Screen Title: Roboto Regular 44sp
  • Browse Category Title: Roboto Condensed 20sp
  • Details Content Titles: Roboto Regular 34sp
  • Details Subtext: Roboto Regular 14sp

いくつかのTVは強いシャープ化とコントラスト設定をデフォルトにしている.
これらの設定では,細い書体にはジャギーが発生し読みづらくなる.
そのため,細い書体はTV用としては避けた方が良い.

Text

TVアプリケーションではテキストを使いすぎないように.
ユーザはTVからおよそ10フィート離れたところに位置するため文字が読みづらい.
そのため,TV上で多くのテキストを読むのは好まれない.

次のポイントをおさえておくこと.

  • テキストはいくつかの塊に分けることで認識しやすくなる
  • 暗い背景に明るいテキストの組み合わせで.このスタイルはTVでは読みやすい
  • 細い部分と太い部分があるようなフォントは避け,簡単なsans-serifのフォントやアンチエイリアシングを使うと読みやすい
  • 絶対サイズよりレイアウトにあわせて相対サイズ変化させる方がよい.また,px指定よりdp/sp指定とするのが良い

AndroidTV Project on AndroidStudio

2014.07.25時点ではAndroid L Preview に対応するPreview SDKを使用する.
build.gradleへSdkVersionを設定.

android {
compileSdkVersion 'android-L'
...
defaultConfig {
applicationId "tv.android.myandroidtv"
minSdkVersion 'L'

AndroidTVアプリケーションの基本構造はPhoneやTabletと同じであり,既存のアプリケーションを変更する形でAndroidTVアプリケーション化することもできる.
ただし,次の2点はAndroidTVの為に必要な対応となる.

  • AndroidTV用のActivityを作成し,AndroidManifestに登録する (Required)
  • AndroidTV向けのUI部品を提供するSupport Librariesの適用 (Optional)

TV Activity

TVで動作するActivityを作成するにはandroid.intent.category.LEANBACK_LAUNCHERカテゴリを持つIntentFilterをマニフェストに宣言する.
このIntentFilterを持つアプリケーションはTVデバイスに対応していると識別され,GooglePlay上でTVアプリケーションで動作可能として扱われる.
たとえadb経由等で強制的にアプリケーションをインストールしてもTVデバイス上のUIにアプリケーションが表示されることはない.

対応するIntentFilterを持つManifest定義は下記.

<activity
android:name="com.example.android.TvActivity"
android:label="@string/app_name"
android:theme="@style/Theme.Leanback">

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>

TV Support Libraries

Preview SDKに含まれているTV向けのライブラリにはAPIとUI部品が含まれている.
これらのライブラリの使用は必須でないものの,強く使用を推奨されている.

  • v17 leanback support library
    • TV向けUI部品, BrowseFragment, DetailsFragment, SearchFragmentが含まれる.
    • SDK Location: <sdk>/extras/android/support/v17/leanback
    • Gradle dependency: com.android.support:leanback-v17:20.0.+
    • Contains Resource: yes
  • v7 recyclerview library
    • 長いリストでもメモリを効率的に使用するための管理クラスを提供する
    • v17 leanback libraryのいくつかがこのライブラリに依存する
    • SDK Location: <sdk>/extras/android/support/v7/recyclerview
    • Gradle dependency: com.android.support:recyclerview-v7:20.0.+
    • Contains resources: no

v17 leanback support libraryはv4 support libraryに依存している.
そのため,v17 leanback support libraryを使うには3つのライブラリを内包する必要がある.

compile 'com.android.support:recyclerview-v7:+'
compile 'com.android.support:leanback-v17:+'
compile 'com.android.support:appcompat-v7:+'

Layout for TV

TVは一般的に約10フィート離れた場所から使用される.
スクリーンのサイズは他のAndroidデバイスよりもはるかに大きいものの,色やディテールはそれに劣る.
これらの要因を念頭においてTVデバイスアプリケーションのレイアウトを作成すること.

Themes

TV向けの基本レイアウトはThemeとして提供される.TV向けのActivityはこのテーマを適用すべきである.

Leanback Theme

Leanback support libraryはActivityの標準テーマTheme.Leanbackを備え,TVアプリケーションとして一貫したビジュアルを提供する.
多くのアプリケーションでこのThemeの使用が推奨される.
次のコードはActivityにLeanback themeを適用する方法.

<activity
android:name="com.example.android.TvActivity"
android:label="@string/app_name"
android:theme="@style/Theme.Leanback">

NoTitleBar Theme

タイトルバーはAndroidアプリケーションとして標準的なUIだが,TVアプリケーションはそうではない.
もしLeanback themeを適用しない場合はNoTitleBar Themeを適用すること.
次のコードはActivityにNoTitleBar Themeを適用する方法.

<activity
android:name="com.example.android.TvActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar">

Layout Structure

TVデバイスには使いやすく,有用なガイドラインがいくつかある.
TVスクリーン向けのレイアウトを構築する際は次のTipsを参考にすること.

  • Landscapeレイアウトとして構築すること.TVスクリーンは常に横向き表示される.
  • 画面の左側または右側にナビゲーションコントロールを配置し,垂直方向のスペースを確保する.
  • セクションごとに分割されたUIを作成し,フラグメントで表現する.
  • 画面スペースはレイアウトを水平方向に展開するためListViewの代わりにGridViewを使用する.
  • Viewを配置する際にはRelativeLayoutやLinearLayoutのViewGroupを使用する.
    このアプローチでシステムのサイズ,整列,アスペクト比,ピクセル密度によってViewの位置を調節できる.
  • 雑然としたUIを避けるために,レイアウトコントロールの間隔は十分にとること.

Overscan

TVには常にフルスクリーン映像を提供するためのユニークな標準仕様がある.
TVはスクリーン全体に映像を広げるためにコンテンツレイアウトの外枠をクリッピングする.
これは一般にOverscanと呼ばれる.

Overscanの影響を考慮し,UIが欠けることなく画面上に表示されることを保証するために,
レイアウトの周囲余白として10%のマージンを設けるべきである.
マージンは上下27dp,左右48dpに変換される.

下記はマージンを設けるサンプルコード.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/base_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginTop="27dp"
android:layout_marginLeft="48dp"
android:layout_marginRight="48dp"
android:layout_marginBottom="27dp">
</LinearLayout>

もしLeanback support libraryのBrowserFragmentや関連するwidgetを使用している場合,
これらは既にOverscan用マージンを設けているため,マージンを指定する必要はない.

Text and Controls Visibility

TVアプリケーションのレイアウトでは離れた場所からでも見やすく,操作しやすいようにテキストや操作性に注意する.
次のTipsで離れた場所からでもそれらを容易にする.

  • テキストは小さく区切り,まとめることで読みやすくなる
  • 暗い背景に明るいテキストを.TV上ではこのスタイルが見やすい
  • 細いフォントを避けること,または細い部分と太い部分がある書体も避ける.
  • シンプルなsans-serifフォントを使用し,アンチエイリアシングを適用すると読みやすくなる.
  • Android標準のフォントを使用すること
  • 全てのViewが10フィート離れた場所からでも十分に見えるよう大きくすること.
    そのためには絶対的レイアウトではなく相対的レイアウトで組み,ピクセル依存ではなく密度依存のサイズ指定をする.

Android標準のフォントを使用するサンプルは下記.

<TextView
android:id="@+id/atext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"/>

Screen Density and Image Resource

一般的な高精細TVのスクリーン解像度は720p, 1080i, 1080p.
TVアプリケーションがターゲットとすべきスクリーンサイズは1920x1080px.
システムは必要に応じてレイアウト要素を720p向けにダウンスケールする.
一般的にダウンスケーリング(ピクセル除去)はレイアウトの品質を下げないが,アップスケーリングは品質を下げ,UXに悪影響となる.

最適な画像スケーリングのために9-patchイメージが提供されている.
もし低品質な画像や小さな画像を使用していた場合,それらはぼやけたり粗くなる.
これらもUXに悪影響を与えるため,代わりに高品質な画像を提供すること.

レイアウトと大きなスクリーンのためのカスタマイズについて,より詳細な情報はDesigning for multiple screensを参照.

Layout Anti-Patterns

TV向けレイアウトを構築するにあたり避けるべきいくつかのアプローチを紹介.
次の内容はうまく動作せず,悪いUXを招く.

  • PhoneまたはTabletレイアウトの再利用. Phone,Tablet向けレイアウトを再利用しないこと.
    PhoneやTabletのファクタ向けに構築されたレイアウトはTVデバイスには適さない.
    TVに適合させるため簡略化する必要がある.
  • ActionBar. PhoneやTabletでは慣例になっているActionBarはTVでは適していない.
    リモコン操作で,ActionBar上にあるオプションメニューやプルダウンメニューにアクセスするのは困難である.
  • ViewPager. スライディングによりスクリーンを行き来するこのUIはTVでは使用しないこと.

Handling Large Bitmap

TVデバイスは他のAndroidデバイスのようにメモリの上限を持っている.
もしアプリケーションで高精細の画像を使うレイアウトを構築する時,または高解像度の画像をいくつも使う場合,
すぐにメモリ上限に達しOutOfMemoryによりが発生する.これらの問題を回避するために次のTipsを参考にする.

  • イメージは表示される時にだけ読み込む.例えばGridViewやGalleryで複数のイメージを表示する時,
    ViewのAdapterのgetView()が呼ばれた時にだけ画像を読み込む.
  • 長時間使用しないBitmapに対してはrecycle()を呼び出す.
  • CollectionでBitmapオブジェクトをメモリ上で確保する場合はWeakReferenceを使用する.
  • ネットワークから画像を取得するにはAsyncTaskを使用し,デバイス上に保存する.
  • ダウンロードした大きな画像は適切なサイズにスケールダウンすること.そのまま表示するとOutOfMemoryエラーにつながる.

ダウンロードしながらスケールダウンするコードは次の通り.

// Get the source image's dimensions
BitmapFactory.Options options = new BitmapFactory.Options();
// This does not download the actual image, just downloads headers.
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(IMAGE_FILE_URL, options);
// The actual width of the image.
int srcWidth = options.outWidth;
// The actual height of the image.
int srcHeight = options.outHeight;

// Only scale if the source is bigger than the width of the destination view.
if(desiredWidth > srcWidth)
desiredWidth = srcWidth;

// Calculate the correct inSampleSize/scale value. This approach helps reduce
// memory use. This value should be a power of 2.
int inSampleSize = 1;
while(srcWidth / 2 > desiredWidth){
srcWidth /= 2;
srcHeight /= 2;
inSampleSize *= 2;
}

float desiredScale = (float) desiredWidth / srcWidth;

// Decode with inSampleSize
options.inJustDecodeBounds = false;
options.inDither = false;
options.inSampleSize = inSampleSize;
options.inScaled = false;
// Ensures the image stays as a 32-bit ARGB_8888 image.
// This preserves image quality.
options.inPreferredConfig = Bitmap.Config.ARGB_8888;

Bitmap sampledSrcBitmap = BitmapFactory.decodeFile(IMAGE_FILE_URL, options);

// Resize
Matrix matrix = new Matrix();
matrix.postScale(desiredScale, desiredScale);
Bitmap scaledBitmap = Bitmap.createBitmap(sampledSrcBitmap, 0, 0,
sampledSrcBitmap.getWidth(), sampledSrcBitmap.getHeight(), matrix, true);
sampledSrcBitmap = null;

// Save
FileOutputStream out = new FileOutputStream(LOCAL_PATH_TO_STORE_IMAGE);
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
scaledBitmap = null;

TVデバイスでアプリケーションのナビゲーションコントロールには限りがある.
TVアプリケーションで有効なナビゲーションを作成するには限定された操作性とわかりやすさを理解すること.
ユーザがタッチ操作の代わりにD-PADを使ってどのように操作するのか特に注意してアプリケーションを作成する必要がある.

D-PAD Navigation

TVデバイス上でユーザはD-PADあるいは矢印キーを使ってリモート操作を行う.
このタイプのコントローラでは移動が上下左右に制限される.
TV向けにカスタマイズされたアプリケーションを提供するには,コントローラでどう操作すれば良いか直ぐに理解できるように作ること.

TVデバイスにおいてD-PADでのナビケーションシステム向けガイドラインは次の通り.

  • 表示されたコントロール全てに対してD-PADナビゲーションが有効であること.
  • スクロールできるリストにフォーカスがある時,D-PAD上下はリストのスクロールになる.
    Enterキーはアイテム選択に,要素にフォーカスしてリストをスクロールできるように保証すること.
  • コントロール間の移動は直感的かつ予測できること.

AndroidフレームワークはD-PADのような指向ナビゲーションにおいてレイアウト要素間の移動を自動でサポートする.
通常,アプリケーションで特別必要な対応は必要ない.
しかし,D-PADを使用したナビゲーションに問題がないかの確認試験は実施すること.
もし画面の中でナビゲーションが困難な箇所を発見したり,通常とは異なる方法で操作させたい場合は,明示的にナビゲーションを指定できる.

次のコードはTextViewがフォーカスを受けた状態で,次にフォーカスを受けるViewを指定した例.

<TextView android:id="@+id/Category1"
android:nextFocusDown="@+id/Category2"\>

次のリストはAndroidのUIウィジェットで有効なナビゲーション属性.

  • nextFocusDown: ダウンナビゲーションでフォーカスを受けるViewを定義
  • nextFocusLeft: レフトナビゲーションでフォーカスを受けるViewを定義
  • nextFocusRight: ライトナビゲーションでフォーカスを受けるViewを定義
  • nextFocusUp: アップナビゲーションでフォーカスを受けるViewを定義

これらの属性はシステムが設定するデフォルトの動作で上手く機能しない場合に限り使用すること.

Focus and Selection

スクリーン上にあるUI要素に思ったようにフォーカスをあてられることがTVナビゲーションを成功させるコツである.
わかり辛い(どのようなアクションがとれるか明確でない)とユーザにはストレスになり早々にアプリケーションから離れる.
同様に,アプリケーション起動後すぐにアクションできるように常にアイテムがフォーカスされていることが大切.

色,サイズ,アニメーションまたはそれらの組み合わせとレイアウトでユーザが次に何のアクションをすれば良いかわかるようにすること.
アプリケーション全体で統一されたフォーカスの見せ方をすること.

AndroidはDrawable State List Resourcesで選択時とフォーカスされた際ののハイライト制御を提供する.
次はボタンにそれらを適用したコード.

<!-- res/drawable/button.xml -->
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@drawable/button_pressed" /> <!-- pressed -->
<item android:state_focused="true"
android:drawable="@drawable/button_focused" /> <!-- focused -->
<item android:state_hovered="true"
android:drawable="@drawable/button_focused" /> <!-- hovered -->
<item android:drawable="@drawable/button_normal" /> <!-- default -->
</selector>

次のレイアウトはこのstate list drawableを適用する例.

<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:background="@drawable/button" />

フォーカスと選択可能なコントロールの周囲にハイライトが見えるように周囲余白を設けること.

BrowseFragment

Leanback support libraryはTVでバイスでメディアカタログを表示・閲覧するためのAPIをいくつか提供する.
音楽,ビデオを閲覧するためのUIを提供するこれらAPIの使い方を紹介する.

Media Browser Layout

Leanback support libraryにはカテゴリとメディアを少ないコードで実現するための主要レイアウトBrowseFragmentクラスを提供する.
どうやってBrowserFragmentでコンテンツをレイアウトするかの例を次ぎに記す.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>

<fragment
android:name="android.support.v17.leanback.app.BrowseFragment"
android:id="@+id/browse_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>

BrowserFragmentにレイアウトパラメータを設定するサンプルコードは下記.

public class BrowseMediaActivity extends Activity {
public static final String TAG ="BrowseActivity";
protected BrowseFragment mBrowseFragment;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.browse_fragment);

final FragmentManager fragmentManager = getFragmentManager();
mBrowseFragment = (BrowseFragment) fragmentManager.findFragmentById(
R.id.browse_fragment);

// Set display parameters for the BrowseFragment
mBrowseFragment.setHeadersState(BrowseFragment.HEADERS_ENABLED);
mBrowseFragment.setTitle(getString(R.string.app_name));
mBrowseFragment.setBadgeDrawable(getResources().getDrawable(R.drawable.ic_launcher));
mBrowseFragment.setBrowseParams(params);

}
}

Displaying Media Lists

BrowseFragmentはAdapterとPresenterでメディアコンテンツカテゴリから閲覧可能なカテゴリとメディア項目を表示できる.

次のコードはPresenterで文字データを表示するサンプル.

public class StringPresenter extends Presenter {
private static final String TAG = "StringPresenter";

public ViewHolder onCreateViewHolder(ViewGroup parent) {
TextView textView = new TextView(parent.getContext());
textView.setFocusable(true);
textView.setFocusableInTouchMode(true);
textView.setBackground(
parent.getContext().getResources().getDrawable(R.drawable.text_bg));
return new ViewHolder(textView);
}

public void onBindViewHolder(ViewHolder viewHolder, Object item) {
((TextView) viewHolder.view).setText(item.toString());
}

public void onUnbindViewHolder(ViewHolder viewHolder) {
// no op
}
}

メディア項目を表示するためにpresenterを構築し,BrowseFragmentにAdapterをアタッチする.
カテゴリとメディア項目をStringPresenterを使って表示するためのAdapterを構築する方法を次に示す.

private ArrayObjectAdapter mRowsAdapter;
private static final int NUM_ROWS = 4;

@Override
protected void onCreate(Bundle savedInstanceState) {
...

buildRowsAdapter();
}

private void buildRowsAdapter() {
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());

for (int i = 0; i < NUM_ROWS; ++i) {
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
new StringPresenter());
listRowAdapter.add("Media Item 1");
listRowAdapter.add("Media Item 2");
listRowAdapter.add("Media Item 3");
HeaderItem header = new HeaderItem(i, "Category " + i, null);
mRowsAdapter.add(new ListRow(header, listRowAdapter));
}

mBrowseFragment.setAdapter(mRowsAdapter);
}

このサンプルコードはAdapterに直接データを埋め込んでいるが,通常はオンラインデータベースやWebサービスからデータを取得する.
Web上のデータを利用してブラウジングするサンプルアプリケーションはAndroid TVを参照.

Updating the Background

TVのメディアブラウジングアプリケーションで興味を誘う視覚効果を追加するために,コンテンツの閲覧を通して背景画像を更新する.
このテクニックはアプリケーションとのインタラクションをよりシネマティックに,楽しいものにする.

Leanback support libraryはTVアプリケーションのActivityが背景を変更できるようにBackgroundManagerクラスを提供する.
次のコードは背景を更新する簡易な方法.

protected void updateBackground(Drawable drawable) {
BackgroundManager.getInstance(this).setDrawable(drawable);
}

メディア一覧でのメディアブラウズアプリケーションはユーザ操作を通して自動で背景が更新される.
これを実現するには,Selection listenerを設定することで背景を更新する.
次はOnItemSelectedListenerクラスで選択イベントをキャッチし,背景を更新するサンプルコード.

protected void clearBackground() {
BackgroundManager.getInstance(this).setDrawable(mDefaultBackground);
}

protected OnItemSelectedListener getDefaultItemSelectedListener() {
return new OnItemSelectedListener() {
@Override
public void onItemSelected(Object item, Row row) {
if (item instanceof Movie ) {
URI uri = ((Movie)item).getBackdropURI();
updateBackground(uri);
} else {
clearBackground();
}
}
};
}

上記はあくまで一例である.
このファンクションを実装する場合,より良いパフォーマンスを得るためにバックグラウンドスレッドで更新すべき.
さらにアイテムをスクロールしながら閲覧させる場合,背景画像をアップデートするのにアイテムの選択から多少のディレイを加えること.
これにより背景画像の過度な更新を回避できる.

DetailsFragment

Leanback support libraryはメディアブラウジングのためのインタフェースクラスを提供する.
このクラスはメディアアイテムの説明やレビュー,そのアイテムに対してのアクション,購入,再生などの付加情報を表示する.
このセクションではメディア項目詳細のためのPresenterクラスの作り方,詳細画面のためのDetailsFragmentの拡張方法を論じる.

以降のサンプルコードではDetailsFragmentのためにActivityを作成しているが,
BrowserFragmentとDetailsFragmentをFragmentトランザクションを使って置き換えることでActivityを1つで済ませることもできる.

Build a Details Presenter

メディアブラウジングフレームワークはLeanback support libraryで提供される.
詳細情報を含めたスクリーン上のデータ表示を制御するためにPresenterオブジェクトを使用できる.
メディア項目詳細のためのPresenterとしてフレームワークはほぼ完全な実装をAbstractDetailsDescriptionPresenterクラスとして提供する.
ViewにデータをバインドするためのonBindDescription()メソッドを実装すること.
次はそのサンプルコード.

public class DetailsDescriptionPresenter
extends AbstractDetailsDescriptionPresenter {

@Override
protected void onBindDescription(ViewHolder viewHolder, Object itemData) {
MyMediaItemDetails details = (MyMediaItemDetails) itemData;
// In a production app, the itemData object contains the information
// needed to display details for the media item:
// viewHolder.getTitle().setText(details.getShortTitle());

// Here we provide static data for testing purposes:
viewHolder.getTitle().setText(itemData.toString());
viewHolder.getSubtitle().setText("2014 Drama TV-14");
viewHolder.getBody().setText("Lorem ipsum dolor sit amet, consectetur "
+ "adipisicing elit, sed do eiusmod tempor incididunt ut labore "
+ " et dolore magna aliqua. Ut enim ad minim veniam, quis "
+ "nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
+ "commodo consequat.");
}
}

Extends the Details Fragment

メディア項目詳細の表示にDetailsFragmentクラスを使用している場合,メディアアイテムへのアクションとプレビューを拡張できる.
また,関連するコンテンツリストとして追加でコンテンツを表示できる.

前章でのPresenterクラスの使い方についてサンプルコードを示す.
メディアアイテムが表示される際のプレビューとアクションの追加を行う.
また,このサンプルは関連するメディアコンテンツのリストも追加している.

public class MediaItemDetailsFragment extends DetailsFragment {
private static final String TAG = "MediaItemDetailsFragment";
private ArrayObjectAdapter mRowsAdapter;

@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState);

buildDetails();
}

private void buildDetails() {
ClassPresenterSelector selector = new ClassPresenterSelector();
// Attach your media item details presenter to the row presenter:
DetailsOverviewRowPresenter rowPresenter =
new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter());

selector.addClassPresenter(DetailsOverviewRow.class, rowPresenter);
selector.addClassPresenter(ListRow.class,
new ListRowPresenter());
mRowsAdapter = new ArrayObjectAdapter(selector);

Resources res = getActivity().getResources();
DetailsOverviewRow detailsOverview = new DetailsOverviewRow(
"Media Item Details");

// Add images and action buttons to the details view
detailsOverview.setImageDrawable(res.getDrawable(R.drawable.jelly_beans));
detailsOverview.addAction(new Action(1, "Buy $9.99"));
detailsOverview.addAction(new Action(2, "Rent $2.99"));
mRowsAdapter.add(detailsOverview);

// Add a Related items row
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
new StringPresenter());
listRowAdapter.add("Media Item 1");
listRowAdapter.add("Media Item 2");
listRowAdapter.add("Media Item 3");
HeaderItem header = new HeaderItem(0, "Related Items", null);
mRowsAdapter.add(new ListRow(header, listRowAdapter));

setAdapter(mRowsAdapter);
}
}

Creating a Details Activity

DetailsFragmentなどのFragmentはActivityに含める必要がある.
ブラウズ用Activityと詳細表示用Activityを分ける場合,Intentを使って後者のActivityを呼び出すことができる.
このセクションでは詳細表示するためのActivityの実装方法について述べる.

まずは詳細表示用のActivityのためのDetailsFragmentを実装したレイアウトを作成する.

<!-- file: res/layout/details.xml -->

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:name="com.example.android.mediabrowser.MediaItemDetailsFragment"
android:id="@+id/details_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

次にActivityを作成し,先ほどのレイアウトを設定する.

public class DetailsActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.details);
}
}

最後に作成したActivityをAndroidManifest.xmlに追加.この時,Leanbackテーマの適用を忘れないこと.

<application>
...

<activity android:name=".DetailsActivity"
android:exported="true"
android:theme="@style/Theme.Leanback"/>

</application>

Listener for Clicked Items

DetailsFragmentを実装した後は,ユーザがメディアアイテムを選択した時にそこへ遷移させるように変更する.
これを実装するにはOnItemClickedListenerをBrowserFragmentに実装し,詳細表示ActivityのIntentを発行させる.

下記は閲覧用Activityでユーザがメディア項目をクリックした時に詳細表示用Activityに遷移させるリスナーを実装したコード.

public class BrowseMediaActivity extends Activity {
...

@Override
protected void onCreate(Bundle savedInstanceState) {
...

// create the media item rows
buildRowsAdapter();

// add a listener for selected items
mBrowseFragment.setOnItemClickedListener(
new OnItemClickedListener() {
@Override
public void onItemClicked(Object item, Row row) {
System.out.println("Media Item clicked: " + item.toString());
Intent intent = new Intent(BrowseMediaActivity.this,
DetailsActivity.class);
// pass the item information
intent.getExtras().putLong("id", item.getId());
startActivity(intent);
}
});
}
}

Adding Search to TV アプリケーション

ユーザはしばしば特定のコンテンツを思い浮かべながらメディアアプリケーションを使用する.
検索インタフェースはコンテンツ一覧から探すより素早く目的のコンテンツを探すことができる方法を提供する.
Leanback support libraryは標準の検索用インタフェースをアプリケーションから使用できるようにクラス群を提供する.
これは他の機能と一貫したUIとボイスインプットによる検索機能を提供する.

Add Search User Interface

メディア閲覧のインタフェースにBrowseFragmentクラスを使用している時,BrowseFragmentに検索アイコンのOnClickListenerを設定できる.
次はそのサンプルコード.

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.browse_activity);

mBrowseFragment = (BrowseFragment)
getFragmentManager().findFragmentById(R.id.browse_fragment);

...

mBrowseFragment.setOnSearchClickedListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(BrowseActivity.this, SearchActivity.class);
startActivity(intent);
}
});

mBrowseFragment.setAdapter(buildAdapter());
}

検索アイコンにはBrowseFragmentのsetSearchAffordanceColor()メソッド色を設定できる.

ユーザが検索アイコンを選択したとき,システムは定義済みIntentを介してSearchActivityを開始する.
検索用ActivityはLinerLayoutベースで検索用Fragmentを内包すること.
このFragmentはSearchFragment.SearchResultProviderインタフェースを実装し検索結果を表示する.
次はSearchFragmentを拡張し,検索インタフェースと検索結果を提供するコードサンプル.

public class MySearchFragment extends SearchFragment
implements SearchFragment.SearchResultProvider {

private static final int SEARCH_DELAY_MS = 300;
private ArrayObjectAdapter mRowsAdapter;
private Handler mHandler = new Handler();
private SearchRunnable mDelayedLoad;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
setSearchResultProvider(this);
setOnItemClickedListener(getDefaultItemClickedListener());
mDelayedLoad = new SearchRunnable();
}

@Override
public ObjectAdapter getResultsAdapter() {
return mRowsAdapter;
}

@Override
public boolean onQueryTextChange(String newQuery) {
mRowsAdapter.clear();
if (!TextUtils.isEmpty(newQuery)) {
mDelayedLoad.setSearchQuery(newQuery);
mHandler.removeCallbacks(mDelayedLoad);
mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
}
return true;
}

@Override
public boolean onQueryTextSubmit(String query) {
mRowsAdapter.clear();
if (!TextUtils.isEmpty(query)) {
mDelayedLoad.setSearchQuery(query);
mHandler.removeCallbacks(mDelayedLoad);
mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
}
return true;
}
}

このサンプルコードは検索実行コードを別スレッドで実行すべきことを示唆している.
このテクニックは時間のかかるクエリ処理がメインスレッドをブロックしないようにする.

Making Recommendations

コンテンツレコメンドはTVのランチャーとしてまず最初にユーザが目にするリストである.
このリストはユーザが手軽にコンテンツを楽しむことを支援する.
あなたのアプリケーションのコンテンツカタログを提供することで,ユーザをあなたのアプリケーションに連れてくる助けにもなる.

Create a Recommendations Service

コンテンツレコメンドはバックグラウンドプロセスで作成される.
あなたのアプリケーションがレコメンドに寄与するために,サービスを作成して周期的にアプリケーションのカタログをシステムレコメンドリストへ追加できる.

次のコードはIntentServiceを拡張してレコメンドサービスを作成する例である.

public class RecommendationsService extends IntentService {
private static final int MAX_RECOMMENDATIONS = 3;

public RecommendationsService() {
super("RecommendationService");
}

@Override
protected void onHandleIntent(Intent intent) {
MovieDatabase database = MovieDatabase.instance(getApplicationContext());
List recommendations = database.recommendations();

int count = 0;

try {
for (Movie movie : recommendations) {
// build the individual content recommendations
buildRecommendation(getApplicationContext(), movie);

if (++count >= MAX_RECOMMENDATIONS) {
break;
}
}
} catch (IOException e) {
Log.e(TAG, "Unable to update recommendation", e);
}
}
}

このサービスをアプリケーションに登録するコード.

<manifest ... >
<application ... >
...

<service android:name=".RecommendationsService"
android:enabled="true" android:exported="true"/>
</application>
</manifest>

Build Recommendations

サービスを開始したらレコメンドを作成しAndroid Frameworkに渡すこと.
Frameworkはレコメンドを特殊スタイル,特殊カテゴリでマークされたNotificationオブジェクトとして受信する.

次のコードは,レコメンドを作成しNotificationManagerへポストするまでのサンプル.

public class RecommendationsService extends IntentService {

...

public Notification buildRecommendation(Context context, Movie movie)
throws IOException {

if (mNotificationManager == null) {
mNotificationManager = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
}

Bundle extras = new Bundle();
if (mBackgroundUri != movie.getBackgroundUri()) {
extras.putString(EXTRA_BACKGROUND_IMAGE_URL, movie.getBackgroundUri());
}

// build the recommendation as a Notification object
Notification notification = new NotificationCompat.BigPictureStyle(
new NotificationCompat.Builder(context)
.setContentTitle(movie.getTitle())
.setContentText(movie.getDescription())
.setPriority(movie.getPriority())
.setOngoing(true)
.setCategory("recommendation")
.setLargeIcon(movie.getImage())
.setSmallIcon(movie.getSmallIcon())
.setContentIntent(buildPendingIntent(movie.getId()))
.setExtras(extras))
.build();

// post the recommendation to the NotificationManager
mNotificationManager.notify(movie.getId(), notification);
mNotificationManager = null;
return notification;
}

private PendingIntent buildPendingIntent(long id) {
Intent detailsIntent = new Intent(this, DetailsActivity.class);
detailsIntent.putExtra("id", id);

TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(DetailsActivity.class);
stackBuilder.addNextIntent(detailsIntent);
// Ensure each PendingIntent is unique
detailsIntent.setAction(Long.toString(id));

PendingIntent intent = stackBuilder.getPendingIntent(
0, PendingIntent.FLAG_UPDATE_CURRENT);
return intent;
}
}

Run Recommendations Service

アプリケーションのレコメンドサービスは現在のレコメンドを作成する為に周期的に実行されるべきである.
そのためにタイマーか一定時間置きにサービスを実行する.
次のコードはBroadcastReceiverを拡張し,12時間毎に定期的に実行するサンプルコードである.

public class BootupReceiver extends BroadcastReceiver {
private static final String TAG = "BootupActivity";

private static final long INITIAL_DELAY = 5000;

@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
scheduleRecommendationUpdate(context);
}
}

private void scheduleRecommendationUpdate(Context context) {
AlarmManager alarmManager = (AlarmManager)context.getSystemService(
Context.ALARM_SERVICE);
Intent recommendationIntent = new Intent(context,
UpdateRecommendationsService.class);
PendingIntent alarmIntent = PendingIntent.getService(context, 0,
recommendationIntent, 0);

alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
INITIAL_DELAY,
AlarmManager.INTERVAL_HALF_DAY,
alarmIntent);
}
}

BroadcastReceiverクラスがTVデバイス開始時に実行されるよう,マニフェストに次のコードを追加する.

<manifest ... >
<application ... >
<receiver android:name=".BootupReceiver" android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>
</manifest>

important: Boot complete通知を受けるにはRECEIVE_BOOT_COMPLETEパーミッションが必要になる.

Games on TV

TVスクリーンはモバイルGame開発者にとって熟考が必要なことがいくつかある.
巨大なスクリーンサイズ,制御方式,複数のユーザが同時にプレイしている可能性.

Display

2つのポイントを念頭に置いておく.
TVスクリーンで開発をする際には画面が共有されているということと,横画面モードでのデザインが必要であること.

Shared display

リビングなどで大人数でGameをする場合,スクリーンが共有されることに依る障害に注意が必要.
たとえば各プレーヤーが情報を隠すことに意味のあるGame(カードGameやストラテジGame)が該当する.

この対応として,1プレイヤーが別プレイヤーの情報を見られなくするためのメカニズムを実装する必要があります.

  • 例えば,ユーザAのカード情報を表示している間はBユーザのカード情報を伏せること.
    Aユーザが操作をしている間Bユーザは画面を見ないようにし,Bユーザが操作をしている間Aユーザは画面を見ないようにする.
    重要なのはAが操作している時にBの情報は画面上から伏せられていること(逆もしかり)
  • あるいは,PhoneやTablet上でそれらの情報を表示して情報を手の中の秘密にすることもできる.

Landscape display

TVは常に横向きで画面回転することができない.そして縦向きは存在しない.
常にGameを横向きとしてデザインすること.

Input Device

TVはタッチインタフェースを持っていない.
これはアプリ制御を理解する上で,Gameを直感的に楽しませるために非常に重要である.
また,コントローラがTVデバイスから分離されたおり,複数プレイヤーからのコントロールを確立することについても障害が多くある.

D-PAD

D-PADがAndroidTVの標準コントローラとなるため,D-PADによる制御方式を基本に設計すること.
プレイヤーが全ての画面でD-PADによる操作ができるようにすること.コアとなるGameプレイ以外にもメニュー,広告への操作も含まれる.

プレイヤーのコントローラとのインタラクションをどのように形成していくかはUX成功の鍵になるかもしれない.

  • Communicate Controller Requirements up Front: Gameプレイにコントローラが必要な旨をPlayStoreに記載すること.
    Game操作にD-PADよりジョイスティックを備えたコントローラの方がより適合する場合はそれを明記する.
    不適当なコントローラを利用するプレイヤーはあなたのアプリ評価を悪くする.
  • Use Consistent Button Mapping:直感的かつ柔軟なボタンマッピングはより良いUXへの鍵となる.
    例えばAボタンはAccept, BボタンはCancelといった具合に.
    さらにはボタンマッピングをカスタマイズさせる柔軟性を持たせることもできる.
    より詳細なボタンマッピングに関する情報はHandling Controller Actionsを参考.
  • Detect Controller Capabilities and Adjust Accordingly: Gameとコントローラを最適化させるために,コントローラの仕様を確認すること.
    例えば,ユーザにコントローラを振って操作させたいつもりでもコントローラが加速度計やジャイロスコープを搭載していなければ動作しない.
    それらを事前に検知して代替可能なコントローラ操作に切り替えることもできる.
    コントローラの仕様について問い合わせる方法の詳細はSupporting Controllers Across Android Versionsを参照.

Back-button behavior

バックボタンはトグル動作させないこと.例えば,メニューのopen/close等.
バックボタンはパンくずリストでの前場面に戻るために使用されるべき.
例えば Game play > Game pause screen > Game main screen > Android home screen といった具合に.

バックボタンは後方への直線的なナビゲーションであるべきで,Gameのメニューを閉じてメインのプレイ画面に戻る操作として使用してもよい.
ナビゲーションについてのより詳細な情報についてはNavigation with Back and Upを参照.
実装を学びたいならProviding Proper Back Navigationを参照.

Handling multiple controllers

マルチプレイヤーに対応する場合,各々のプレイヤーが持つコントローラペアを意識するのは重要.
どのようにしてコントローラを識別するか,実装についてはInput Devicesを参照.

Handling disconnects

Gameプレイ中にコントローラの接続が解除された場合はGameを一時停止し,その旨をユーザにダイアログで伝えること.

ダイアログはトラブルシューティングとして表示する(例えばBluetooth接続を確認させるダイアログをポップアップ表示するなど).
より詳細な入力デバイスに関する情報はをSupporting Game Controllers参照.
Bluetooth接続はBluetoothを参照.

Manifest

ランチャーでGameはそれ専用のリスト上に表示される.
AndroidTVはandroid:isGameフラグを使用してGameかそうでないアプリかを区別する.
下記はisGameフラグを指定するサンプルソース.

<application>
...
< android:isGame=["true" | "false"] >
...
</application>

Google Play Game Services

作成するGameをGooglePlayGameServiceに統合させる場合は実績,サインオン,セーブ,マルチプレイヤー等いくつか熟考すべきことがある.

Achievements

Gameには得ることができる実績を含んできる必要がある.
Gameをコントロールしたユーザだけが実績を得るべき.実績の実装方法についてはAchievements in Androidを参照.

Sign-in

Game起動時にユーザのサインインを試みるように.
ユーザがサインインを数回断るようであればその試みはやめるべき.
サインインについての情報はImplementing Sign-in on Androidを参照.

Saving

Gameデータの保存はPlayServiceのCloudSaveを推奨する.
GameのセーブデータはGoogleアカウントと紐づけられ,複数のデバイスで個人を識別できる.
Phone,TabletやTVからでもアカウント情報からセーブデータをpullできる.

アプリはGameプレイヤーにローカルとクラウドそれぞれに保存されたデータの削除を許容するUIをもうけるべき.
あなたはGame中の設定画面にこの機能を配置するかもしれない.これらの実装方法についてはCloud Save in Androidを参照.

Multiplayer experience

マルチプレイ可能なGameは少なくとも2人以上のプレイヤーが参加する.
AndroidでのマルチプレイヤーGameについての詳細情報は[Real-time Multiplayer]https://developers.google.com/games/services/android/realtimeMultiplayer)とTurn-based Multiplayerを参照.

Web

GoogleAndroidチームはGameにWebブラウジングを持ち込むことを推奨しない.
それはTVスクリーンやリモコンでWebブラウジングするには適していないためである.
note: WebViewをGoogle+やFacebookへのログインサービスとして使用することはできる.

Hardware Feature

TVは他のAndroidデバイスにあるものを搭載していない.
例えばタッチスクリーン,カメラ,GPSは多くのデバイスに搭載されるがTVには搭載されていない.
TV向けアプリケーションを作成する際にはこれらの機能がなくても動作するように設計すること.

ここでは有効でないHardware Feature制限下でどうやってTVアプリケーションを動作させるかを議論する.

Unsupported Hardware Features

TVは他のデバイスとは異なる目的を持つ.そして他のAndroidデバイスでは通常持ち得るHardwareを持っていない.
下記はサポートしていないHardware feature.

  • Camera: android.hardware.camera
  • GPS: android.hardware.location.gps
  • Microphone: android.hardware.microphone
  • NFC: android.hardware.nfc
  • Telephony: android.hardware.telephony
  • Touchscreen: android.hardware.touchscreen

Checking Available Features

実行環境で有効なFeatureを確認するにはhasSystemFeature(String)を呼ぶ.
このメソッドは単一のStringを引数にとり,指定されたfeatureをチェックできる.
たとえば,タッチスクリーンが有効であるかを確認したい場合は引数にFEATURE_TOUCHSCREENを指定する.

下記は実行環境でHardware featureの有効チェックを行うコード.

// Check if the telephony hardware feature is available.
if (getPackageManager().hasSystemFeature("android.hardware.telephony")) {
Log.d("Mobile Test", "Running on phone");
// Check if android.hardware.touchscreen feature is available.
} else if (getPackageManager().hasSystemFeature("android.hardware.touchscreen")) {
Log.d("Tablet Test", "Running on devices that don't support telephony but "+
"do have a touch screen.");
} else {
Log.d("TV Test", "Running on a TV!");
}

UiModeManager.getCurrentModeType()メソッドを利用して現在のプラットフォームの種類を判断できる.
TVでバイス上ではこのメソッドはConfiguration.UI_MODE_TYPE_TELEVISIONを返す.

Checking Available and Handling Unsupported Features

アプリケーションが特定のHardware featureが有効でなくなった場合の対処法.

Touch screen

AndroidTVではタッチスクリーンをサポートしない.
そもそもTVは10フィート離れて座った状態から使用されるためこの要件はマッチしない.
TVデバイスではこの制限に対してDirectionPad(D-PAD)によるリモートコントロールを採用する.
より詳しいTVコントロールの情報はNavigation for TVを参照.

アプリケーションがタッチスクリーンFeatureを必要とするかどうかについては次のコードをマニフェストに定義する.

<uses-feature android:name="android.hardware.touchscreen"
android:required="false"/>

Camera

TVは一般的にカメラを備えていない.
もし,写真撮影アプリケーションの開発でカメラ機能の搭載を必須とせず,写真の閲覧と編集機能だけでも提供したいような場合は,
カメラ機能をアプリケーション動作の必須要件としないようにマニフェストへ次の定義を追加する.

<uses-feature android:name="android.hardware.camera" android:required="false" />

もし,アプリケーションにカメラ機能が搭載されていた場合に有効としたい機能がある場合,
次のカメラの有効性をチェックする方法が有効.

// Check if the camera hardware feature is available.
if (getPackageManager().hasSystemFeature("android.hardware.camera")) {
Log.d("Camera test", "Camera available!");
} else {
Log.d("Camera test", "No camera available. View and edit features only.");
}

GPS

TVは屋内据え付けのデバイスでありGPS受信機を持たない.
アプリケーションがロケーション情報を必要とする場合,ユーザが場所を検索するか,
TVセットアップ時に設定されるZIPコードを対象とするStaticLocationProviderを使用することができる.

LocationManager locationManager = (LocationManager) this.getSystemService(
Context.LOCATION_SERVICE);
Location location = locationManager.getLastKnownLocation("static");
Geocoder geocoder = new Geocoder(this);
Address address = null;

try {
address = geocoder.getFromLocation(location.getLatitude(),
location.getLongitude(), 1).get(0);
Log.d("Zip code", address.getPostalCode());

} catch (IOException e) {
Log.e(TAG, "Geocoder error", e);
}

Portions of this page are modifications based on work created and shared by the Android Open Source Project
and used according to terms described in the Creative Commons 2.5 Attribution License.

AndroidTV:Recommendation Item

$
0
0

Recommendation Item

環境:Android L Developer Preview.

AndroidTVのレコメンドリストにアイテムを追加する.

Recommendationの登録

レコメンドはNotificationオブジェクトで追加する.

Notification notification = new NotificationCompat.BigPictureStyle(
new NotificationCompat.Builder(mContext)
.setContentTitle(mTitle)
.setContentText(mDescription)
.setPriority(mPriority)
.setLocalOnly(true)
.setOngoing(false)
.setColor(mContext.getResources().getColor(R.color.fastlane_background))
.setCategory("recommendation")
.setLargeIcon(image)
.setSmallIcon(mSmallIcon)
.setContentIntent(mIntent)
.setExtras(extras))

レコメンドとするNotificationのカテゴリには”recommendation”を指定する.
登録したレコメンドはランチャーのレコメンドリスト(下図)に追加される.

1:レコメンドにフォーカスした状態.
2:レコメンドにフォーカスしていない状態

コンテンツテキストはレコメンドにフォーカス中のみ表示される.

Recommendationの要素

Notification情報がRecommendationでどのように表示されるか.
次はNotificationプロパティとRecommendation要素の対応表.

左図(レコメンドフォーカス時)
1: LargeIcon
2: Color(帯の背景色)
3: ContentTitle
4: ContentText
5: SmallIcon

右図(レコメンド非フォーカス時)
1: LargeIcon
2: (システムにより指定される帯の背景色)
3: ContentTitle
4: SmallIcon

SmallIconの配色を考える際は,背景色が1色とは限らない点に注意.

AndroidTV:ObjectAdapter, Presenter

$
0
0

Table of contents

Introduction

Android leanback support libraryには, メディアコンテンツを閲覧表示するためのAPIが備わっている.
ここではObjectAdapterPresenterについて記載する.

Overview

Android leanback support libraryはコンテンツ(Model)と表示(View)の責務を分離するPresenterの仕組みを提供する.
またModelとPresenterの接続役となるObjectAdapterも提供される.

ObjectAdapterはデータ(メディアコンテンツ等)と, PresenterSelectorを内包する抽象クラス.
BrowserFragment, DetailsFragment(あるいはそれらのサブクラス)のsetAdapter(ObjectAdapter)でObjectAdapterを登録する.
PresenterSelectorはModel毎に最適なPresenterを選択し, PresenterがこれをViewにバインドする.

Model-View-Presenter Pattern

本稿についてはModel-View-Presenter Patternについて知っておくと理解が早い.


AndroidTVでは, メディアコンテンツがModelに, PresenterがPresenterに, ViewがViewにそれぞれ対応する.
ObjectAdapterはModelとPresenterを対応づけるアダプタとして作用する.

APIs

ObjectAdapter

android.support.v17.leanback.widget.ObjectAdapter

Class Overview

Adapter for leanback activities. Provides access to a data model and is decoupled from the presentation of the items via PresenterSelector.

Leanback Activity用のAdapter. Modelへのアクセス手段を提供し, PresenterSelectorでModelとViewを分離する.

ObjectAdapterは抽象クラスであり, Modelを管理する手段を提供しない.
ModelをArrayListで管理する場合はサブクラスのArrayObjectAdapterを使用する.

// Set Presenter
ArrayObjectAdapter adapter =
newArrayObjectAdapter(newMyStringPresenter());

// Add Models
adapter
.add("Media Item 1");
adapter
.add("Media Item 2");
adapter
.add("Media Item 3");

ObjectAdapterにPresenterSelectorを設定する主な方法は次の3パターン.

  1. ObjectAdapterのコンストラクタ引数にPresenterSelectorインスタンスを渡す
  2. ObjectAdapterのコンストラクタ引数にPresenterインスタンスを渡す
  3. ObjectAdapter.setPresenterSelectorメソッドでPresenterSelectorをセットする
// 1. Set Presenter
ArrayObjectAdapter adapter1 =newArrayObjectAdapter(
newMyStringPresenter());

// 2. Set PresenterSelector
ArrayObjectAdapter adapter2 =newArrayObjectAdapter(
newSinglePresenterSelector(newMyStringPresenter()));

// 3. Lazy initialize
ArrayObjectAdapter adapter3 =newArrayObjectAdapter();
adapter3
.setPresenterSelector(
newSinglePresenterSelector(newMyStringPresenter()));

Summary

下記は主なAPI.

ObjectAdapter(PresenterSelector presenterSelector)
Construct an adapter with the given PresenterSelector.
Adapterを初期化し, PresenterSelectorを設定する
ObjectAdapter(Presenter presenter)
Construct an adapter that uses the given Presenter for all items.
Adapterを初期化し, 全てのリスト項目にPresenterを使用する.内部的にはSinglePresenterSelectorが設定される.
ObjectAdapter.DataObserver
A DataObserver can be notified when an ObjectAdapter’s underlying data changes.
DataObserverはObjectAdapterのデータに変更があった場合に通知を受け取ることができる
get(int position)
Returns the item for the given position.
引数positionに位置するitem(Model)を返却
getId(int position)
Returns id for the given position.
引数positionに位置するidを返却
getPresenter(Object item)
Returns the Presenter for the given item from the adapter.
item(Model)に対応するPresenterを返却
setPresenterSelector(PresenterSelector presenterSelector)
Set the presenter selector.
PresenterSelectorを設定する

PresenterSelector

android.support.v17.leanback.widget.PresenterSelector

Class Overview

A PresenterSelector is used to obtain a Presenter for a given Object.

PresenterSelectorは対象Modelに対応するPresenterを取得するために使用される.

ObjectAdapterがModelをViewへバインドするためのPresenterの選択に使用される.
Presenterオブジェクトを取得するために使用される基本的なPresenterSelectorは次の通り.

  • ClassPresenterSelector
  • SinglePresenterSelector

Summary

getPresenter(Object item)
Returns a presenter for the given item.
item(Model)に対応するPresenterを返却する.

SinglePresenterSelector

android.support.v17.leanback.widget.PresenterSelector
 ┗ndroid.support.v17.leanback.widget.SinglePresenterSelector

Class Overview

常に同じPresenterを返すPresenterSelector. Modelが全て同じタイプである場合に使用する.

publicfinalclassSinglePresenterSelectorextendsPresenterSelector{
privatefinalPresenter mPresenter;

publicSinglePresenterSelector(Presenter presenter){
mPresenter
= presenter;
}

@Override
publicPresenter getPresenter(Object item){
return mPresenter;
}
}

Summary

SinglePresenterSelector(Presenter presenter)
The Presenter to return for every item.
全てのModel共通で使用されるPresenterを指定して初期化
getPresenter(Object item)
Returns a presenter for the given item.
指定されたitem(Model)に対応するPresenterを返却する. 常に同じPresenterを返却する.

ClassPresenterSelector

android.support.v17.leanback.widget.PresenterSelector
 ┗android.support.v17.leanback.widget.ClassPresenterSelector

Class Overview

Modelの内容に基づいてPresenterクラスを切り替えたい場合に使用する.
ClassPresenterSelectorはClassをキーに持つMapでPresenterを管理する.

publicfinalclassClassPresenterSelectorextendsPresenterSelector{
privatefinalHashMap<Class<?>,Presenter> mClassMap =newHashMap<Class<?>,Presenter>();

publicvoid addClassPresenter(Class<?> cls,Presenter presenter){
mClassMap
.put(cls, presenter);
}

@Override
publicPresenter getPresenter(Object item){
// item.getClass() にマッチするPresenterをmClassMapから見つけ出し返却する
}
}

Summary

addClassPresenter(Class cls, Presenter presenter)
(no description)
Modelの型に対応するPresenterを登録する
getPresenter(Object item)
Returns a presenter for the given item.
指定されたitem(Model)に対応するPresenterを返却する.

Presenter

android.support.v17.leanback.widget.Presenter

Class Overview

A Presenter is used to generate Views and bind Objects to them on demand. It is closely related to concept of an RecyclerView.Adapter, but is not position-based.

PresenterはViewを生成しObjectをバインドする.
これはRecyclerview.Adapterのコンセプトと近似しているがpositionベースではない.

A trivial Presenter that takes a string and renders it into a TextView:

下記はPresenterがStringを受け取りそれをTextViewにレンダリングするコード:

publicclassStringTextViewPresenterextendsPresenter{
// This class does not need a custom ViewHolder, since it does not use
// a complex layout.
@Override
publicViewHolder onCreateViewHolder(ViewGroup parent){
returnnewViewHolder(newTextView(parent.getContext()));
}

@Override
publicvoid onBindViewHolder(ViewHolder viewHolder,Object item){
String str =(String) item;
TextView textView =(TextView) viewHolder.mView;
textView
.setText(item);
}

@Override
publicvoid onUnbindViewHolder(ViewHolder viewHolder){
// Nothing to unbind for TextView, but if this viewHolder had
// allocated bitmaps, they can be released here.
}
}

Summary

主なAPIは下記.

onBindViewHolder(Presenter.ViewHolder viewHolder, Object item)
Binds a View to an item.
item(Model)をViewにバインドする
onCreateViewHolder(ViewGroup parent)
Creates a new View.
新しいViewを作成する
onUnbindViewHolder(Presenter.ViewHolder viewHolder)
Unbinds a View from an item. Any expensive references may be released here, and any fields that are not bound for every item should be cleared here.
Viewからitem(Model)をアンバインドする. Viewにバインドされていないものも含め強参照はここでリリースされるべき.

Presenter.ViewHolder

android.support.v17.leanback.widget.Presenter.ViewHolder

Class Overview

ViewHolder can be subclassed and used to cache any view accessors needed to improve binding performance (for example, results of findViewById) without needing to subclass a View.

ViewHolderはサブクラス化することができる.(その他は一般的なViewHolderと同じ)

Summary

特記事項なし

License:
Portions of this page are modifications based on work created and shared by the Android Open Source Project
and used according to terms described in the Creative Commons 2.5 Attribution License.

“Model View Presenter GUI Design Pattern” by Google - http://www.gwtproject.org/articles/testing_methodologies_using_gwt.html. Licensed under Creative Commons Attribution-Share Alike 3.0 via Wikimedia Commons - http://commons.wikimedia.org/wiki/File:Model_View_Presenter_GUI_Design_Pattern.png#mediaviewer/File:Model_View_Presenter_GUI_Design_Pattern.png

AndroidTV:Row, RowPresenter

$
0
0

Table of contents

Introduction

Android leanback support libraryにはコンテンツ表示用のAPIがいくつか提供されている.
ここではコンテンツのヘッダを表現するクラス群について記載する.

Overview

メディアコンテンツを一覧表示する際には”カテゴリ”といったメタ情報を表示し, グルーピングするのが一般的である.
Android leanback support libraryはメタ情報をヘッダとして扱い, 抽象化する.


[カテゴリ毎にグルーピングされたメディアコンテンツ]

Row header

コンテンツリストの”行”はRowクラスで表現され, メタ情報はHeaderItemとしてRowに保持される.
RowがModelに対応し, RowPresenterがModelをViewへバインドするPresenterとして作用する.
Rowは”行”(および行見出し)として振る舞うだけで, “行”に紐づく子要素を持たない.

Rowに(先述のような)Menu Item 1, 2, 3 … を追加したければ, サブクラスのListRowクラスを使用する.

APIs

Row

android.support.v17.leanback.widget.Row

Class Overview

A row in a RowsFragment. This is the base class for all rows. You will typically use a ListRow, but you may override this class for non-list Rows.

“行”を表現するクラス. これはすべての”行”の基底クラスとなる.
ただし, 非リスト表現のためにこのクラスを拡張することもできる.

Summary

下記は主なAPI.

Row(HeaderItem headerItem)
Constructor for a Row.
行を初期化し, HeaderItemをセットする.
getHeaderItem()
Get the HeaderItem that represents metadata for the row.
行のメタ情報表現であるHeaderItemを返却する
setHeaderItem(HeaderItem headerItem)
Set the HeaderItem that represents metadata for the row.
行のメタ情報表現であるHeaderItemを設定する
getId()

Returns a unique identifier for this row. This id can come from one of three places:

  • If setId(long) is ever called on this row, it will return this id.
  • If setId(long) has not been called but the header item is not null, the result of HeaderItem.getId() is returned.
  • Otherwise ObjectAdapter#NO_ID is returned.

行を特定するユニークなIDを返却する.IDは次の3つの内から返される.

  • setId(long)が呼ばれている場合は, この行のIDを返却する
  • setId(long)が呼ばれておらず, HeaderItemが存在する場合はHeaderItem.getId()の値を返却する
  • 上記以外の場合はObjectAdapter#NO_IDを返却する
setId(long id)
Set the id for this row.
行を特定するユニークIDを設定する

HeaderItem

android.support.v17.leanback.widget.HeaderItem

Class Overview

A header item is an item that describes metadata of Row, such as a category of media items. Developer may override this class to add more information.

ヘッダは, メディアコンテンツの”カテゴリ”にあたるメタ情報の表現.
メタ情報を追加したい場合はこのクラスを拡張する.

Summary

下記は主なAPI.

HeaderItem(long id, String name, String imageUri)
Create a header item.
ヘッダの初期化
getId()
Returns a unique identifier for this item.
このヘッダのユニークIDを返却する
getImageUri()
Returns the icon for this header item.
このヘッダのアイコンを返却する
getName()
Returns the name of this header item.
このヘッダの名前を返却する

RowPresenter

android.support.v17.leanback.widget.Presenter
 ┗android.support.v17.leanback.widget.RowPresenter

Class Overview

An abstract Presenter that renders a Row.

Rowをレンダリング対象とする抽象的なPresenter

Customize UI widgets

When a subclass of RowPresenter adds UI widgets, it should subclass RowPresenter.ViewHolder and override createRowViewHolder(ViewGroup) and initializeRowViewHolder(ViewHolder).

RowPresenterを継承しUIウィジェットを追加する場合, そのサブクラスはRowPresenter.ViewHolderを継承し, createRowViewHolder(ViewGroup)メソッドとinitializeRowViewHolder(ViewHolder)メソッドをオーバライドすべき.

The subclass must use layout id “row_content” for the widget that will be aligned to the title of any HeadersFragment that may exist in the parent fragment.

サブクラスはUIウィジェットのレイアウトIDにrow_contentを使用すること.
これにより親Fragmentとして存在し得るHeadersFragmentにより, タイトルに沿って行が配置される.

RowPresenter contains an optional and replaceable RowHeaderPresenter that renders the header. You can disable the default rendering or replace the Presenter with a new header presenter by calling setHeaderPresenter(RowHeaderPresenter).

RowPresenterはヘッダをレンダリングする置換可能なRowHeaderPresenterを持つことがある.
setHeaderPresenter(RowHeaderPresenter)を使用することで, 標準レンダラを無効化するか, あるいは別のレンダラに置き換えることもできる.

UI events from fragments

RowPresenter receives calls from its parent (typically a Fragment) when:

RowPresenterは次のケースで(通常Fragmentから)呼び出される

  • A Row is selected via setRowViewSelected(Presenter.ViewHolder, boolean). The event is triggered immediately when there is a row selection change before the selection animation is started. Subclasses of RowPresenter may override onRowViewSelected(ViewHolder, boolean).

setRowViewSelected(Presenter.ViewHolder, boolean)で行が選択された場合.
行を選択する際, 選択アニメーションが始まるよりも前にイベントは発行される.
RowPresenterのサブクラスはonRowViewSelected(ViewHolder, boolean)メソッドをoverrideできる.

  • A Row is expanded to full width via setRowViewExpanded(Presenter.ViewHolder, boolean). The event is triggered immediately before the expand animation is started. Subclasses of RowPresenter may override onRowViewExpanded(ViewHolder, boolean).

setRowViewExpanded(Presenter.ViewHolder, boolean)で行が展開された場合.
展開アニメーションよりも前にイベントは発行される.
RowPresenterのサブクラスはonRowViewExpanded(ViewHolder, boolean)メソッドをoverrideできる.

User events

RowPresenter provides OnItemSelectedListener and OnItemClickedListener. If a subclass wants to add its own View.OnFocusChangeListener or View.OnClickListener, it must do that in createRowViewHolder(ViewGroup) to be properly chained by the library. Adding View listeners after createRowViewHolder(ViewGroup) is undefined and may result in incorrect behavior by the library’s listeners.

RowPresenteronItemSelectedListenerOnItemClickedListenerを提供する.
もしサブクラスがView.OnFocusChangeListenerまたはView.OnClickListenerを追加したい場合はcreateRowViewHolder(ViewGroup)で行う.
createRowViewHolder(ViewGroup)は後続処理のため適切にoverrideすること. createRowViewHolder(ViewGroup)の後でViewのリスナーを追加しても正しく動作しない可能性がある.

Selection animation

When a user scrolls through rows, a fragment will initiate animation and call setSelectLevel(Presenter.ViewHolder, float) with float value between 0 and 1. By default, the RowPresenter draws a dim overlay on top of the row view for views that are not selected.

ユーザが行をスクロールした場合, Fragmentはアニメーションを生成しsetSlectLevel(Presenter.ViewHolder, float)メソッドを呼び出す. float値は0(unselected)~1(selected)の間.
RowPresenterは標準で選択されていない行に対してdimエフェクトをオーバレイ描画する.


[dimming effect off/on]

Subclasses may override this default effect by having isUsingDefaultSelectEffect() return false and overriding onSelectLevelChanged(ViewHolder) to apply a different selection effect.

サブクラスはisUsingDefaultSelectEffect()でfalseを返し, onSelectLevelChanged(ViewHolder)をoverrideして異なる選択エフェクトを適用することができる.

Call setSelectEffectEnabled(boolean) to enable/disable the select effect, This will not only enable/disable the default dim effect but also subclasses must respect this flag as well.

setSelectEffectEnabled(boolean)でdimエフェクトのon/offを選択できる.
これはデフォルトのdimエフェクトをon/offするにとどまらない. サブクラスはこのフラグに注意すること.

Summary

下記は主なAPI.

getSelectEffectEnabled()
Returns true if the row selection effect is enabled. This value not only determines whether the default dim implementation is used, but subclasses must also respect this flag.
行の選択エフェクトが有効である場合はtrueが返却される. このフラグはデフォルトdimエフェクトを使うかどうかだけでは決定されないため, サブクラスはこのフラグに注意すること.
setSelectEffectEnabled(boolean applyDimOnSelect)
Enables or disables the row selection effect. This will not only affect the default dim effect, but subclasses must respect this flag as well.
選択エフェクトを有効あるいは無効にする. このフラグの影響はデフォルトdimエフェクトに留まらない.
サブクラスはこのフラグに注意すること.
isUsingDefaultSelectEffect()
Return whether this RowPresenter is using the default dimming effect provided by the library. Subclasses may(most likely) return false and override onSelectLevelChanged(ViewHolder).
RowPresenterがライブラリ標準のdimエフェクトを提供しているかどうか.
サブクラスはonSelectLevelChanged(ViewHolder)をoverrideして選択エフェクトを再定義する場合はfalseを返すこと.
setOnItemClickedListener(OnItemClickedListener listener)
Set the listener for item click events. A RowPresenter does not use this listener, but a subclass may fire an item click event if it has the concept of an item. The OnItemClickedListener will override any View.OnClickListener that an item’s Presenter sets during onCreateViewHolder(ViewGroup). So in general, you should choose to use an OnItemClickedListener or a View.OnClickListener, but not both.
アイテムのクリックイベントリスナを設定する. RowPresenterはこのリスナを使用しないが, サブクラスはクリックイベントの処理をここに定義できる.
OnItemClickedListenerは, PresenterによりonCreateViewHolder(ViewGroup)の中でoverrideされたView.OnClickListenerでセットされます.
一般的にOnItemClickedListenerまたはView.OnClickListenerのいずれかが選択されます.
setOnItemSelectedListener(OnItemSelectedListener listener)
Set the listener for item or row selection. A RowPresenter fires a row selection event with a null item. Subclasses (e.g. ListRowPresenter) can fire a selection event with the selected item.
行,またはアイテム選択のリスナを設定する. RowPresenterは性質上, 空アイテムの選択でこのイベントを発行する.
サブクラス(例えばListRowPresenter)はアイテムが選択されたらイベントを発行できる.

License:
Portions of this page are modifications based on work created and shared by the Android Open Source Project
and used according to terms described in the Creative Commons 2.5 Attribution License.

AndroidTV:ListRow, ListRowPresenter

$
0
0

Table of contents

Introduction

Android leanback support libraryにはコンテンツ表示用のAPIがいくつか備わっている.
ここではコンテンツを水平方向に並べるAPIについて記載する.

Overview

AndroidTVのデザインガイドラインには, 画面上下はカテゴリ等のセクション領域として確保する方針が書かれている.
そのためコンテンツのリストは垂直展開するListViewより水平展開するGridViewが好まれる.


[カテゴリ毎のメディアコンテンツリスト]

Horizontal list

Android leanback support libraryではコンテンツをリスト管理するためのクラスを提供する.

  • ListRow
  • ListRowPresenter

下記はこれらを使用したサンプルコード.

privateArrayObjectAdapter mRowsAdapter;
privatestaticfinalint NUM_ROWS =4;

privatevoid buildRowsAdapter(){
mRowsAdapter
=newArrayObjectAdapter(newListRowPresenter());

for(int i =0; i < NUM_ROWS;++i){
ArrayObjectAdapter listRowAdapter =newArrayObjectAdapter(
newStringPresenter());
listRowAdapter
.add("Media Item 1");
listRowAdapter
.add("Media Item 2");
listRowAdapter
.add("Media Item 3");
HeaderItem header =newHeaderItem(i,"Category "+ i,null);
mRowsAdapter
.add(newListRow(header, listRowAdapter));
}

mBrowseFragment
.setAdapter(mRowsAdapter);
}

ListRowはRowのサブクラスであるため, HeaderItemを持つ.
更にHeaderItemに紐づくコンテンツを子要素として管理するよう拡張されている.

下図はListRowの構造.


[ListRow]

ListRowは子要素をObjectArrayで保持する.
上記サンプルコードではStringStringPresenterのセット(listRowAdapter)になる.
※StringPresenterは別途用意したString専用のPresenter

下図は子要素の構造.


[Child Elements]

これら行リストは水平方向へ展開するためにしばしばHorizontalGridViewにバインドされる.

Vertical list

ListRowは行のリスト表現である.
ここにヘッダ(カテゴリ等)を追加し, 列リストを形成するためにListRowをArrayObjectAdapterで管理する.
これにより複数行リスト(列×行)を作成し, ListRowを列リストとして表現する.

ListRowがModelで, ListRowPresenterがPresenterとして作用する.
最終的なデータ構造は次の通り.


[ListRows]

これら列リストは垂直方向へ展開するためにしばしばVertialGridViewにバインドされる.

APIs

ListRow

android.support.v17.leanback.widget.Row
 ┗android.support.v17.leanback.widget.ListRow

Class Overview

A row composed of a optional HeaderItem, and an ObjectAdapter describing children.

HeaderItem(任意)と, 子要素が定義されたObjectAdapterから構成される.

Summary

ListRow(HeaderItem header, ObjectAdapter adapter)
(no discription)
ListRowを初期化する. 行のメタ情報となるHeaderItemと, コンテンツをリスト表示するObjectAdapterを引数に取る
getAdapter()
Get the ObjectAdapter that represents a list of objects.
コンテンツをリスト表示するObjectAdapterを返却する

ListRowPresenter

android.support.v17.leanback.widget.Presenter
 ┗android.support.v17.leanback.widget.RowPresenter
   ┗android.support.v17.leanback.widget.ListRowPresenter

Class Overview

ListRowPresenter renders ListRow using a HorizontalGridView hosted in a ListRowView.

ListRowPresenterはListRowをレンダリングする. ListRowはHorizontalGridViewを持つListRowViewにバインドされる.

Hover card

Optionally, setHoverCardPresenterSelector(PresenterSelector) can be used to display a view for the currently focused list item below the rendered list. This view is known as a hover card.

任意で, フォーカスされたアイテムのPresenterSelectorを指定するsetHoverCardPresenterSelector(PresenterSelector)が利用できる.
例えばフォーカスしたアイテムをHover cardのように見せることができる.

Selection animation

ListRowPresenter disables RowPresenter’s default dimming effect and draw a dim overlay on top of each individual child items. Subclass may override and disable isUsingDefaultListSelectEffect() and write its own dim effect in onSelectLevelChanged(RowPresenter.ViewHolder).

ListRowPresenterはRowPresenterの標準エフェクトを無効化し, フォーカスされていない各行のコンテンツリストにオーバレイの効果を描画する.
サブクラスはisUsingDefaultListSelectEffect()をoverrideしてこれを無効化し, onSelectLevelChanged(RowPresenter.ViewHolder)でエフェクトを再定義できる.

Shadow

ListRowPresenter applies a default shadow to child of each view. Call setShadowEnabled(boolean) to disable shadow. Subclass may override and return false in isUsingDefaultShadow() and replace with its own shadow implementation.

ListRowPresenterは標準でViewに影の効果を与える. setShadowEnabled(boolean)でこれを無効化できる.
サブクラスはisUsingDefaultShadow()でfalseを返し,独自の影の効果を実装できる.

Summary

下記は主なAPI.

ListRowPresenter (int zoomFactor)
Constructs a ListRowPresenter with the given parameters.
zoomFactor: Controls the zoom factor used when an item view is focused. One of ZOOM_FACTOR_NONE, ZOOM_FACTOR_SMALL, ZOOM_FACTOR_MEDIUM, ZOOM_FACTOR_LARGE
ListRowPresenterを初期化する.
引数zoomFactorにはViewがフォーカスされた際のズーム係数を指定する. 係数は次の中から選択する(ZOOM_FACTOR_NONE, ZOOM_FACTOR_SMALL, ZOOM_FACTOR_MEDIUM, ZOOM_FACTOR_LARGE)
setHoverCardPresenterSelector(PresenterSelector selector)
Set PresenterSelector used for showing a select object in a hover card.
選択されたアイテムをHover cardとして表示するためのPresenterSelectorを設定する
getHoverCardPresenterSelector()
Get PresenterSelector used for showing a select object in a hover card.
選択されたアイテムをHolver cardとして表示するためのPresenterSelectorを取得する
getShadowEnabled()
Returns true if child shadow is enabled.
子要素の影効果が有効になっている場合はtrueを返却する
setShadowEnabled(boolean enabled)
Enable or disable child shadow.
子要素の影効果を有効/無効で設定する
isUsingDefaultShadow()
Returns true if SDK >= 18, where default shadow is applied to each individual child of HorizontalGridView.
SDKバージョンが18以上の場合にtrueを返却する. 標準の影効果はHorizontalGridViewの子要素として個別に充てられる.

License:
Portions of this page are modifications based on work created and shared by the Android Open Source Project
and used according to terms described in the Creative Commons 2.5 Attribution License.


Android:Google Play In-app Billing

$
0
0

Table of contents

Introduction

AndroidではGoogle Playサービスを利用してアプリ内課金を比較的容易に導入できます.
本稿ではビジネス戦略の1つ”アプリ内課金”にフォーカスしたAndroid Developers | Google Play In-app Billingを翻訳し, サービス概略について学びます.

Google Play In-app Billing

In-app Billing is a Google Play service that lets you sell digital content from inside your applications. You can use the service to sell a wide range of content, including downloadable content such as media files or photos, virtual content such as game levels or potions, premium services and features, and more. You can use In-app Billing to sell products as

  • Standard in-app products (one-time billing), or
  • Subscriptions, (recurring, automated billing)

In-app BillingはGoogle Playのサービスを通して, アプリ内部でデジタルコンテンツを販売できるアプリ内課金サービスである.
音楽,動画,写真といったダウンロード可能なコンテンツ, ゲームのレベルやポーションといった仮想コンテンツ, プレミアムサービスや特別な機能など販売できるコンテンツは多岐に渡る.
アプリ内課金サービスでの販売形態には次の2点がある.

-スポット課金(その場限りの請求, こちらが標準的である)
-定期購読(繰り返し自動課金)

When you use the in-app billing service to sell an item, whether it’s an in-app product or a subscription, Google Play handles all checkout details so your application never has to directly process any financial transactions. Google Play uses the same checkout backend service as is used for application purchases, so your users experience a consistent and familiar purchase flow.

アプリ内課金サービスを使って商品を売る時, スポット課金か定期購読どちらでもGoogle Playはアプリが直接決済処理に関わる必要が無いよう肩代わりする.
決済フローについてはアプリ購入時のそれと同じものを使用するため, 馴染みあるユーザ体験を提供する.

Any application that you publish through Google Play can implement In-app Billing. No special account or registration is required other than a Google Play Developer Console account and a Google Wallet merchant account.

Google Playで公開されるアプリのほとんどでアプリ内課金の実装が可能である.
アプリ内課金の実装に, Google Play Developer ConsoleアカウントとGoogle Wallet merchantアカウント以外のアカウントは不要.

To help you integrate in-app billing into your application, the Android SDK provides a sample application that demonstrates how to sell standard in-app products and subscriptions from inside an app.

アプリにアプリ内課金を統合するための助けとして, スポット課金と定期購読の方法をデモンストレーションするサンプルアプリをAndroid SDKで提供している.

In-app Billing Overview

This documentation describes the fundamental In-app Billing components and features that you need to understand in order to add In-app Billing features into your application.

本章はアプリ内課金を導入するにあたって理解しておく必要のある基本的なコンポーネントと機能について記載する.

Note: Ensure that you comply with applicable laws in the countries where you distribute apps. For example, in EU countries, laws based on the Unfair Commercial Practices Directive prohibit direct exhortations to children to buy advertised products or to persuade their parents or other adults to buy advertised products for them. See the position of the EU consumer protection authorities for more information on this and other topics.

Note:
アプリを配布する際には, 必ずその国の法律に従うこと.
例えば, EU諸国では消費者に対する商業上の不公正行為に関する消費者保護法Unfair Commercial Practices Directive(UCPD)を採用しており, 子供を対象とした広告や, 親(他の大人含め)を説得させて購入させる(押し売りやミスリーディングを誘う)ような行為は禁止されている.
これに関するより詳細な情報はposition of the EU consumer protection authoritiesを参照.

In-app Billing API

Your application accesses the In-app Billing service using an API that is exposed by the Google Play app that is installed on the device. The Google Play app then conveys billing requests and responses between your application and the Google Play server. In practice, your application never directly communicates with the Google Play server. Instead, your application sends billing requests to the Google Play application over interprocess communication (IPC) and receives responses from the Google Play app. Your application does not manage any network connections between itself and the Google Play server.

アプリ内課金サービスへのアクセスは端末にインストールされているGoogle PlayのAPIを使用する. アプリとGoogle Playサーバ間の決済処理はGoogle Playアプリが橋渡しを担うため, アプリが直接Google Playサーバと直接通信する必要がなく, ネットワークコネクションを管理する必要がない.
アプリとGoogle Playアプリはプロセス間通信(IPC)で決済リクエストを送信し, レスポンスを受信する.

In-app Billing can be implemented only in applications that you publish through Google Play. To complete in-app purchase requests, the Google Play app must be able to access the Google Play server over the network.

アプリ内課金はGoogle Playで公開されているアプリでのみ使用できる.
商品の購入は, Google PlayアプリがGoogle Playサーバとの通信をもって完結する.

In-app billing Version 3 is the latest version, and maintains very broad compatibility across the range of Android devices. In-app Billing Version 3 is supported on devices running Android 2.2 or higher that have the latest version of the Google Play store installed (a vast majority of active devices).

In-app Billing Ver.3は最新のバージョンで, 殆どのAndroidデバイスに互換性を持つ.
Ver.3はAndroid2.2以上, Google Play Ver.3.9.16以上で動作する(a vast majority of active devices).

Version 3 features

  • Requests are sent through a streamlined API that allows you to easily request product details from Google Play, order in-app products, and quickly restore items based on users’ product ownership
  • Order information is synchronously propagated to the device on purchase completion
  • All purchases are “managed” (that is, Google Play keeps track of the user’s ownership of in-app products). The user cannot own multiple copies of an in-app item; only one copy can be owned at any point in time
  • Purchased items can be consumed. When consumed, the item reverts to the “unowned” state and can be purchased again from Google Play
  • Provides support for subscriptions

For details about other versions of In-app Billing, see the Version Notes.

  • Ver.3はGoogle Playにある商品情報のリクエスト, アプリ内商品の注文や, 商品所有情報からアイテムリストを復元するのに最適化されたAPIを提供する.
  • 注文情報は購入完了時に各端末と同期される
  • 購入情報は全て管理されている(つまりGoogle Playはユーザのアプリ内商品の所有状態を追跡している). ユーザは同じ商品のコピーを複数持つことはできず, 1つしか所持できない仕組みである.
  • 購入した商品は消費できる. 商品を消費すると所有状態ではなくなり, 再び商品を購入できるようになる.
  • Ver.3は定期購読をサポートする

In-app Products

In-app products are the digital goods that you offer for sale from inside your application to users. Examples of digital goods includes in-game currency, application feature upgrades that enhance the user experience, and new content for your application.

アプリ内商品はアプリ内で提供されるデジタル商品である.
例えばゲーム内通貨や, アプリ機能を拡張するアップグレードや, アプリ向け新コンテンツなどがこれに含まれる.

You can use In-app Billing to sell only digital content. You cannot use In-app Billing to sell physical goods, personal services, or anything that requires physical delivery. Unlike with priced applications, once the user has purchased an in-app product there is no refund window.

アプリ内課金はデジタルコンテンツにのみ対応し, 物理的な物の売買や配送, 個人サービスについては扱わない. また, アプリ購入の時とは異なり商品の返品画面は用意されない.

Google Play does not provide any form of content delivery. You are responsible for delivering the digital content that you sell in your applications. In-app products are always explicitly associated with one and only one app. That is, one application cannot purchase an in-app product published for another app, even if they are from the same developer.

Google Playは商品について, 任意の配信方法を許可していない. 商品はアプリ内で販売する必要がある. また, アプリ内商品はアプリと対で管理される.
そのため, たとえ同じ開発者のアプリであっても商品(情報含め)を共有することはできない.

Product types

In-app Billing supports different product types to give you flexibility in how you monetize your application. In all cases, you define your products using the Google Play Developer Console.

アプリ内課金は異なる種類の商品に対応し, アプリのマネタイズに柔軟に対応できる.
全てのケースで, 全ての商品をGoogle Play Developer Consoleで定義する.

You can specify these types of products for your In-app Billing application — managed in-app products and subscriptions. Google Play handles and tracks ownership for in-app products and subscriptions on your application on a per user account basis. Learn more about the product types supported by In-app Billing Version 3.

課金アプリで商品の種類としてアプリ内商品定期購読品かを指定できる.
Google Playはアプリのユーザアカウント毎に双方を処理し, 所有状態を追跡する.

Google Play Developer Console

The Developer Console is where you can publish your In-app Billing application, and manage the various in-app products that are available for purchase from your application.

Developer Consoleはアプリを公開し, アプリで購入可能な商品を管理する場所である.

You can create a product list of digital goods that are associated with your application, including items for one-time purchase and recurring subscriptions. For each item, you can define information such as the item’s unique product ID (also called its SKU), product type, pricing, description, and how Google Play should handle and track purchases for that product.

アプリに紐づくスポット購入と定期購読されるデジタル商品のプロダクトリストを作成できる. どちらも, 商品のユニーク商品ID(SKUと呼ばれる), 商品種別, 価格, 説明を定義し, Google Playにどのように扱わせて, 商品購入を追跡させるかを定義できる.
(SKU: Stock Keeping Unit. 最小管理単位)

You can also create test accounts to authorize access for testing applications that are unpublished.

アプリをテストするために, テストアカウントを作成し公開前のアプリでテストすることができる.

To learn how to use the Developer Console to configure your in-app products and product list, see Administering In-app Billing.

Developer Consoleでアプリ内商品とプロダクトリストを設定する方法についてはAdministering In-app Billingを参照.

Google Play Purchase Flow

Google Play uses the same checkout backend service as is used for application purchases, so your users experience a consistent and familiar purchase flow.

Google Playの決済フローはアプリ購入時のそれと同じものを使用するため, 馴染みあるユーザ体験を提供する.

Important: You must have a Google Wallet merchant account to use the In-app Billing service on Google Play.

重要:
Google Playでアプリ内課金サービスを利用するにはGoogle Wallet merchantアカウントが必要になる.

To initiate a purchase, your application sends a billing request for a specific in-app product. Google Play then handles all of the checkout details for the transaction, including requesting and validating the form of payment and processing the financial transaction.

決済を始めるにあたり, アプリは特定商品の決済リクエストを送信する.
Google Playは決済リクエスト, 決済フォームのバリデート, 金融決済処理を含む全ての決済処理をハンドリングする.

When the checkout process is complete, Google Play sends your application the purchase details, such as the order number, the order date and time, and the price paid. At no point does your application have to handle any financial transactions; that role is provided by Google Play.

決済処理が完了した時, Google Playは注文番号, 注文日時, 支払い金額といった決済情報の詳細をアプリに通知する.
この決済処理の中でアプリが意識するべき所ははく, Google Playが全てを肩代わりする.

Sample Application

To help you integrate In-app Billing into your application, the Android SDK provides a sample application that demonstrates how to sell in-app products and subscriptions from inside an app.

アプリにアプリ内課金を統合するための助けとして, スポット課金と定期購読の方法をデモンストレーションするサンプルアプリをAndroid SDKで提供している.

The TrivialDrive sample for the Version 3 API sample shows how to use the In-app Billing Version 3 API to implement in-app product and subscription purchases for a driving game. The application demonstrates how to send In-app Billing requests, and handle synchronous responses from Google Play. The application also shows how to record item consumption with the API. The Version 3 sample includes convenience classes for processing In-app Billing operations as well as perform automatic signature verification.

TrivialDrive sample for the Version 3 APIサンプルはIn-app Billing Version3 APIを使用し, ドライビングゲームを通してスポット課金と定期購読の決済方法を紹介する.
また, Google Playからの同期レスポンスの捌き方について実演する.
サンプルはAPIで商品の消費がどのように記録されるかも示す.
Ver.3のサンプルは課金処理のための便利クラスと自動署名検証機能も持っている.

Recommendation: Make sure to obfuscate the code in your application before you publish it. For more information, see Security and Design.

Recommendation:アプリを公開する際にはソースコードを難読化すること.
より詳細な情報はSecurity and Designを参照.

Migration Considerations

If you have an existing In-app Billing implementation that uses Version 2 or earlier, it is strongly recommended that you migrate to In-app Billing Version 3 at your earliest convenience.

もしVer.2でアプリ課金を実装しているものが既にあるならIn-app Billing Version 3への移行を強く推奨する.

If you have published apps selling in-app products, note that:

もし, すでにアプリ内商品を販売している場合は, 次のことに留意する:

  • Managed items and subscriptions that you have previously defined in the Developer Console will work with Version 3 as before.
  • Unmanaged items that you have defined for existing applications will be treated as managed products if you make a purchase request for these items using the Version 3 API. You do not need to create a new product entry in Developer Console for these items, and you can use the same product IDs to purchase these items. They will still continue to be treated as unmanaged items if you make a purchase request for them using the Version 2 or earlier API.
  • Ver.2の頃, Developer Consoleで定義した商品や定期購読はVer.3でも同じように扱われる
  • 既にあるアプリで定義された管理されていない商品は, Ver.3のAPIを使用すると管理された商品として扱われる. これらのアイテムをDeveloper Consoleで新たに作成する必要はなく, 同じ商品IDを使ってこれらを購入することができる. これらの商品はVer.2(またはそれ以前)のAPIで購入リクエストを作成する際は, 引き続き管理されていない商品として扱われる.

License:
Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.

Android:In-app Billing Version3

$
0
0

Introduction

AndroidではGoogle Playサービスを利用してアプリ内課金を比較的容易に導入できます.
本稿ではビジネス戦略の1つ”アプリ内課金”にフォーカスしたAndroid Developers | In-app Billing Version 3を翻訳し, In-app Billing Ver.3 APIについて学びます.

In-app Billing Version 3

The In-app Billing Version 3 API makes it easier for you to integrate In-app Billing into your applications. The features in this version include improved synchronous purchase flow, APIs to let you easily track ownership of consumable goods, and local caching of in-app purchase data.

In-app Billing Ver.3 APIを使うことでアプリ内課金の仕組みをアプリに組み込めます.
このバージョンでは, 過去Verから更に改善された購入フロー, 商品所有情報を追跡するAPI, アプリ内課金情報のローカルキャッシュ機能が含まれる.

Product Types

You define your products using the Google Play Developer Console, including product type, SKU, price, description, and so on. For more information, see Administering In-app Billing. The Version 3 API supports managed in-app products and subscriptions.

Google Play Developer Consoleで定義した商品には, 商品種別, SKU, 価格等が含まれる.
より詳細な情報はAdministering In-app Billingを参照.
Ver.3 APIはアプリ内商品の管理と定期購読をサポートする.

Managed In-app Products

Managed in-app products are items that have their ownership information tracked and managed by Google Play. When a user purchases a managed in-app item, Google Play stores the purchase information for each item on a per-user basis. This enables you to later query Google Play at any time to restore the state of the items a specific user has purchased. This information is persistent on the Google Play servers even if the user uninstalls the application or if they change devices.

Google Playはユーザが購入した商品の所有情報を追跡する.
商品を購入すると, ユーザ毎の購入情報が記録される.
これにより, 特定ユーザの購入履歴から商品の所有状態をいつでも復元できる.
この情報はGoogle Playサーバに保管されており, アプリのアンインストールや端末変更でも引き続き利用(復元)できる.

If you are using the Version 3 API, you can also consume managed items within your application. You would typically implement consumption for items that can be purchased multiple times (such as in-game currency, fuel, or magic spells). Once purchased, a managed item cannot be purchased again until you consume the item, by sending a consumption request to Google Play. To learn more about in-app product consumption, see Consuming Items

Ver.3 APIを使用すると, 商品の消費についても管理できる.
商品の消費は複数回購入できる商品(ゲーム内通貨, ポーション, 魔法の書など)で実装されるのが一般的.
一度商品を購入すると, Google Playに消費リクエストを送信して商品を消費するまで同じ商品は購入できなくなる.
商品の消費についてより詳細な情報はConsuming Itemsを参照.

Subscriptions

A subscription is a product type offered in In-app Billing that lets you sell content, services, or features to users from inside your app with recurring monthly or annual billing. You can sell subscriptions to almost any type of digital content, from any type of app or game. To understand how subscriptions work, see In-app Billing Subscriptions.

定期購読はアプリ内のコンテンツ, サービス, または機能を毎月or毎年定期的に購読(課金)させる支払い形態の一種である.
アプリやゲームからデジタルコンテンツを定期購読販売できる.

With the Version 3 API, you can use the same purchase flow for buying subscriptions and retrieving subscription purchase information as with in-app products. For a code example, see Implementing Subscriptions.

Important: Unlike in-app products, subscriptions cannot be consumed.

Ver.3 APIでは定期購読の購入に同じ支払いフローを使用し, アプリ内商品と同じように定期購読の購入履歴から復元できる.
コードのサンプルはImplementing Subscriptionsを参照.

Important: Google Playで管理されない商品や定期購読は対象外.

Purchasing Items

A typical purchase flow with the Version 3 API is as follows:

Ver.3 APIを使用した購入フロー:

The basic sequence for a purchase request.
[The basic sequence for a purchase request.]

  1. Your application sends a isBillingSupported request to Google Play to determine that the target version of the In-app Billing API that you are using is supported.
  2. When your application starts or user logs in, it’s good practice to check with Google Play to determine what items are owned by the user. To query the user’s in-app purchases, send a getPurchases request. If the request is successful, Google Play returns a Bundle containing a list of product IDs of the purchased items, a list of the individual purchase details, and a list of the signatures for the purchases.
  3. Usually, you’ll want to inform the user of the products that are available for purchase. To query the details of the in-app products that you defined in Google Play, your application can send a getSkuDetails request. You must specify a list of product IDs in the query request. If the request is successful, Google Play returns a Bundle containing product details including the product’s price, title, description, and the purchase type.
  4. If an in-app product is not owned by the user, you can initiate a purchase for it. To start a purchase request, your application sends a getBuyIntent request, specifying the product ID of the item to purchase, along with other parameters. You should record the product ID when you create a new in-app product in the Developer Console.
    1. Google Play returns a Bundle that contains a PendingIntent which you application uses to start the checkout UI for the purchase.
    2. Your application launches the pending intent by calling the startIntentSenderForResult method.
    3. When the checkout flow finishes (that is, the user successfully purchases the item or cancels the purchase), Google Play sends a response Intent to your onActivityResult method. The result code of the onActivityResult has a result code that indicates whether the purchase was successful or canceled. The response Intent contains information about the purchased item, including a purchaseToken String that is generated by Google Play to uniquely identify this purchase transaction. The Intent also contains the signature of the purchase, signed with your private developer key.

To learn more about the Version 3 API calls and server responses, see In-app Billing Reference.

  1. 使用するIn-app Billing APIのバージョンがサポートされているかGoogle Playに問い合わせる(isBillingSupported)
  2. ユーザの購入情報を取得(getPurchases)する.
    購入情報はBundleに詰めて返され, 商品ID, 購入詳細, 購入署名それぞれがリストで返される.
    購入情報を取得するタイミングとしてアプリ起動時やログイン時を推奨する.
  3. ユーザに商品を訴求するために商品詳細を取得(getSkuDetails)する.
    取得リクエストには商品IDを指定する.
    商品詳細はBundleに詰めて返され, 商品価格, タイトル, 説明, 購入種別が含まれる.
  4. ユーザが商品を所有していない場合に購入フローを開始(getBuyIntent)できる. パラメータには商品IDを指定する. Developer Consoleで作成した商品のIDは記録しておくこと.
    1. getBuyIntentで返却されるBundleには購入手続きを開始するためのPendingIntentが含まれている.
    2. アプリはstartIntentSenderForResultメソッドを呼び出してIntent起動する.
    3. 支払いフローが完了(購入完了 or 購入キャンセル)するとGoogle Playは購入結果をIntentに詰めてonActivityResultメソッドを呼ぶ.
      onActivityResultの結果コードは購入の成功/失敗を示す. Intentには購入情報(商品情報や購入トランザクションを一意に識別するpurchaseTokenの文字列)が含まれる.
      Intentには開発者の秘密鍵で署名された購入証明書も含まれる.

Ver.3 APIとサーバレスポンスについてのより詳細な情報はIn-app Billing Referenceを参照.

Consuming In-app Products

You can use the consumption mechanism to track the user’s ownership of in-app products.

In Version 3, all in-app products are managed. This means that the user’s ownership of all in-app item purchases is maintained by Google Play, and your application can query the user’s purchase information when needed. When the user successfully purchases an in-app product, that purchase is recorded in Google Play. Once an in-app product is purchased, it is considered to be “owned”. In-app products in the “owned” state cannot be purchased from Google Play. You must send a consumption request for the “owned” in-app product before Google Play makes it available for purchase again. Consuming the in-app product reverts it to the “unowned” state, and discards the previous purchase data.

ユーザの商品所持状態を追跡するために, 消費メカニズムを使用する.

Ver.3ではGoogle Playによって全ての商品や購入情報が管理されており, 必要に応じてこれらの情報を問い合わせることができる. ユーザの購入情報はGoogle Playに記録される.
一度商品を購入し”所持状態”になると, 消費リクエストをGoogle Playに送信するまで同じ商品を購入することはできない. 商品が消費されると”未所持状態”に戻り, 以前の購入データは破棄される.

The basic sequence for a consumption request.
[The basic sequence for a consumption request.]

To retrieve the list of product’s owned by the user, your application sends a getPurchases call to Google Play. Your application can make a consumption request by sending a consumePurchase call. In the request argument, you must specify the in-app product’s unique purchaseToken String that you obtained from Google Play when it was purchased. Google Play returns a status code indicating if the consumption was recorded successfully.

ユーザが所持する商品リストを取得するにはgetPurchasesメソッドを, 商品を消費するにはconsumePurchaseメソッドを呼び出す. メソッド引数には商品の購入完了時にGoogle Playから払い出される商品の識別文字列purchaseTokenを指定する.

Non-consumable and Consumable In-app Products

It’s up to you to decide if you want to handle your in-app products as non-consumable or consumable items.

商品種別を非消費系とするか消費系とするかは開発者が自由に設定できる.

Non-consumable Items
Typically, you would not implement consumption for in-app products that can only be purchased once in your application and provide a permanent benefit. Once purchased, these items will be permanently associated to the user’s Google account. An example of a non-consumable in-app product is a premium upgrade or a level pack.
非消費系の商品
非消費系の商品は, 一度購入すれば永続的に効果が持続するため通常選択されない.
一度購入された非消費系の商品は購入ユーザのGoogleアカウントに紐づく. 実装されるケースとしてはアプリのプレミアムアップグレードやレベルパックのような商品に適用できる.
Consumable items
In contrast, you can implement consumption for items that can be made available for purchase multiple times. Typically, these items provide certain temporary effects. For example, the user’s in-game character might gain life points or gain extra gold coins in their inventory. Dispensing the benefits or effects of the purchased item in your application is called provisioning the in-app product. You are responsible for controlling and tracking how in-app products are provisioned to the users.
Important: Before provisioning the consumable in-app product in your application, you must send a consumption request to Google Play and receive a successful response indicating that the consumption was recorded.
消費系の商品
非消費系の商品とは対照的に, 消費系の商品は同じ商品を複数回購入できるように消費を実装します.
消費系商品の効果は通常一時的なものになる. 例えば, ゲームのポーションや課金によるゲーム内通貨の追加など.
購入商品の利益や効果を分配することを商品の供給と呼ぶ. 開発者は商品がユーザに供給されているかを追跡し制御する責務を負う.
Important:アプリで消費系商品を供給する前に, 必ず消費リクエストをGoogle Playに投げて消費リクエストの成功(消費が正しく記録されたことも意味する)を受け取ること.

Managing consumable purchases in your application

Here is the basic flow for purchasing a consumable in-app product:

  1. Launch a purchase flow with a getBuyIntent call
  2. Get a response Bundle from Google Play indicating if the purchase completed successfully.
  3. If the purchase was successful, consume the purchase by making a consumePurchase call.
  4. Get a response code from Google Play indicating if the consumption completed successfully.
  5. If the consumption was successful, provision the product in your application.

消費系商品を購入する基本フロー:

  1. getBuyIntentを呼び出し, 購入基本フローを開始する
  2. 購入が成功するとGoogle Playからレスポンスを受け取りBundleを取得する
  3. 購入が完了したらconsumePurchaseで商品を消費する
  4. 消費が完了したかどうかを示すレスポンスコードを取得する
  5. 消費が成功したならば, 商品を供給する

Subsequently, when the user starts up or logs in to your application, you should check if the user owns any outstanding consumable in-app products; if so, make sure to consume and provision those items. Here’s the recommended application startup flow if you implement consumable in-app products in your application:

続いて, アプリケーションを起動するかログインするかの時, 消費していない商品があるならこれを消費, 供給する.
次はアプリ起動時に推奨される消費のフローである:

  1. Send a getPurchases request to query the owned in-app products for the user.
  2. If there are any consumable in-app products, consume the items by calling consumePurchase. This step is necessary because the application might have completed the purchase order for the consumable item, but stopped or got disconnected before the application had the chance to send a consumption request.
  3. Get a response code from Google Play indicating if the consumption completed successfully.
  4. If the consumption was successful, provision the product in your application.
  1. getPurchasesリクエストを送り, ユーザが所有する商品を問い合わせる
  2. 消費系の商品があったならば, consumePurchaseで商品を消費する. このステップは商品を購入したにも関わらず消費処理が未完了となっている可能性に備えて必要となる.
  3. Google Playから消費リクエストの成功レスポンスを受け取る
  4. 消費リクエストが成功していたならば商品の共有を行う

Local Caching

Because the Google Play client now caches In-app Billing information locally on the device, you can use the Version 3 API to query for this information more frequently, for example through a getPurchases call. Unlike with previous versions of the API, many Version 3 API calls will be serviced through cache lookups instead of through a network connection to Google Play, which significantly speeds up the API’s response time.

Ver.3 APIを使用している場合, Google Playクライアントはアプリ内課金の情報をデバイスローカルにキャッシュしている. これにより, getPurchasesなど情報の問い合わせ速度がネットワーク接続を必要としないため, 過去APIバージョンより格段に応答速度が改善し, 問い合わせの頻度を多くすることを可能とする.


License:
Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.

Android:Google Play In-App Subscriptions

$
0
0

Introduction

AndroidではGoogle Playサービスを利用してアプリ内課金を比較的容易に導入できます.
本稿ではビジネス戦略の1つ”アプリ内課金”にフォーカスしたAndroid Developers | Google Play In-App Subscriptionsを翻訳し, サブスクリプションについて学びます.

Google Play In-App Subscriptions

Subscriptions let you sell content, services, or features in your app with automated, recurring billing. You can easily adapt an existing In-app Billing implementation to sell subscriptions.

サブスクリプションはコンテンツ, サービス, 機能を定期的に自動で購入させるサービスであり, 簡単にアプリへ組み込める.

This document is focused on highlighting implementation details that are specific to subscriptions, along with some strategies for the associated billing and business models.

本稿では, サブスクリプションの詳細と課金戦略, ビジネスモデルにフォーカスする.

Overview of Subscriptions

A subscription is a product type offered in In-app Billing that lets you sell content, services, or features to users from inside your app with recurring monthly or annual billing. You can sell subscriptions to almost any type of digital content, from any type of app or game.

サブスクリプションはアプリ内課金商品の一種で, コンテンツ, サービス, 機能を毎月, 毎年といった単位で定期的に自動購入させるサービスである.
サブスクリプションはほとんどの商品に適用できる.

As with other in-app products, you configure and publish subscriptions using the Developer Console and then sell them from inside apps installed on Android devices. In the Developer console, you create subscription products and add them to a product list, then set a price and optional trial period for each, choose a billing interval (monthly or annual), and then publish. For more information about using the Developer Console, see Configuring Subscription Items.

他の商品と同じようにサブスクリプションもDeveloper Consoleで設定し, Androidにインストールされたアプリ内で販売される.
Developer Consoleでサブスクリプションを作成し, プロダクトリストに追加し, 価格を決めて, オプションでお試し期間を設定し, 自動課金される期間(毎月 or 毎年)を設定して公開する.
Developer Consoleについてのより詳細な情報はConfiguring Subscription Itemsを参照.

When users purchase subscriptions in your apps, Google Play handles all checkout details so your apps never have to directly process any financial transactions. Google Play processes all payments for subscriptions through Google Wallet, just as it does for standard in-app products and app purchases. This ensures a consistent and familiar purchase flow for your users.

ユーザによるサブスクリプションの購入にあたってはGoogle Playが勘定に関する処理を行うため, アプリがこれに関与する必要がない.
Google Playはサブスクリプションの支払い処理を通常の商品と同じくGoogle Walletを使用して行う.
購入のフローはアプリや, 他の商品購入時と一貫したものになる.

enter image description here

After users have purchase subscriptions, they can view the subscriptions and cancel them from the My Apps screen in the Play Store app or from the app’s product details page in the Play Store app. For more information about handling user cancellations, see Subscription Cancellation.

購入したサブスクリプションはGoogle Playのアプリ詳細画面か, マイストアから購読を停止することができる.
購読のキャンセルについてより詳細な情報はSubscription Cancellationを参照.

In adddition to client-side API calls, you can use the server-side API for In-app Billing to provide subscription purchasers with extended access to content (for example, from your web site or another service). The server-side API lets you validate the status of a subscription when users sign into your other services. For more information about the API, see Purchase Status API.

アプリ内課金のサブスクリプションについては, クライアントサイドAPIに加えてサーバサイドAPIも提供されている.
サーバサイドAPIはAndroid以外の独自サービスからも使用でき, サブスクリプションのステータスを取得することができる.

You can also build on your existing external subscriber base from inside your Android apps.

  • If you sell subscriptions on a web site, for example, you can add your own business logic to your Android app to determine whether the user has already purchased a subscription elsewhere, then allow access to your content if so or offer a subscription purchase from Google Play if not.
  • You can implement your own solution for sharing subscriptions across as many different apps or products as you want. For example, you could sell a subscription that gives a subscriber access to an entire collection of apps, games, or other content for a monthly or annual fee. To implement this solution, you could add your own business logic to your app to determine whether the user has already purchased a given subscription and if so, allow access to your content.

外部サービスで提供しているサブスクリプションを購入済みなユーザへの配慮.

  • Webサイトで独自のサブスクリプションを販売しているような場合, 独自のビジネスロジックをアプリに追加しユーザが既にサブスクリプションを購入済みであるかどうかを確認する. 購入済みであるなら独自コンテンツを提供するし, そうでないならGoogle Playから購入させる.
  • 異なるアプリや商品を跨いでサブスクリプションを共有するために独自のソリューションを実装できる.
    例えば, 月額or年額の手数料を払えばアプリやゲームのコレクションを自由に遊べるサブスクリプションを売りたいとする.
    これを実現するためには, アプリにユーザが既にサブスクリプションを購入済みかどうかを判断するビジネスロジックを組み込み, その上でコンテンツにアクセスさせる.

In general the same basic policies and terms apply to subscriptions as to standard in-app products, however there are some differences. For complete information about the current policies and terms, please read the policies document.

To learn about the minimum system requirements for subscriptions, see the Version Notes.

サブスクリプションについての基本的なポリシーや条件は, 標準のアプリ内商品にも同じことが言える.
細部では異なる部分もあるので, 詳細はpolicies documentを参照.

サブスクリプションについての最小要件についてはVersion Notesを参照.

Configuring Subscription Items

To create and manage subscriptions, use the Developer Console to set up a product list for the app then configure these attributes for each subscription product:

  • Purchase Type: always set to Subscription
  • Subscription ID: An identifier for the subscription
  • Publishing State: Unpublished/Published
  • Language: The default language for displaying the subscription
  • Title: The title of the subscription product
  • Description: Details that tell the user about the subscription
  • Price: USD price of subscription per recurrence
  • Recurrence: monthly or yearly
  • Additional currency pricing (can be auto-filled)

For details on how to add and configure products in the Developer Console, see Administering In-app Billing.

サブスクリプションの作成と管理について, アプリで使用できるようにDeveloper Consoleを使って商品リストをセットアップする.
作成, 管理にあたりサブスクリプションの設定項目には次がある:

  • 購入種別: 常にSubscriptionを設定する
  • サブスクリプションID: サブスクリプションの識別子
  • 公開状態: 公開/非公開
  • 言語: サブスクリプションを表示する際のデフォルト言語設定
  • タイトル: サブスクリプションのタイトル
  • 説明: サブスクリプションについての説明文
  • デフォルト価格: USD価格(毎月or毎年単位で支払う額)
  • 支払い間隔: 月単位 or 年単位
  • 追加の価格(USDから価格オートフィルされます)

Developer Consoleでの商品設定について詳細な情報はAdministering In-app Billingを参照.

Subscription pricing

When you create a subscription in the Developer Console, you can set a price for it in any available currencies. Each subscription must have a non-zero price. You can price multiple subscriptions for the same content differently — for example you could offer a discount on an annual subscription relative to the monthly equivalent.

Important: To change the price of a subscription, you can publish a new subscription product ID at a new price, then offer it in your app instead of the original product. Users who have already purchased will continue to be charged at the original price, but new users will be charged at the new price.

Developer Consoleでサブスクリプションを作成するときに(その時有効な通貨で)価格を設定できるが, どのサブスクリプションも有料である必要がある.
1つのサブスクリプションに対して異なる価格を設定できる. 例えば, 購読期間に応じて, 月毎に課金額を割引くようなサービスで使える.

Important:サブスクリプションの価格を変更すると, 新しい価格設定のサブスクリプションIDが払い出され, オリジナルの商品と置き換えられる.
価格変更前にこれを購入していたユーザはオリジナル価格が引き継がれ, 価格変更後にこれを購入したユーザは変更後の価格が反映される.

User billing

In the Developer Console, you can configure subscription products with automated recurring billing at either of two intervals:

  • Monthly — Google Play bills the customer’s Google Wallet account at the time of purchase and monthly subsequent to the purchase date (exact billing intervals can vary slightly over time)
  • Annually — Google Play bills the customer’s Google Wallet account at the time of purchase and again on the same date in subsequent years.

Developer Consoleでサブスクリプションの支払い間隔として次の2つが設定できる.

  • 毎月 — Google Playは商品を購読しているユーザのGoogle Walletアカウントに対して毎月請求する.
    (厳密にいうと, 請求される間隔は正確に一ヶ月とはならないかもしれない)
  • 毎年 — Google Playは商品を購読しているユーザのGoogle Walletアカウントに対して毎年請求する.
    (翌年の同月日となる)

Billing continues indefinitely at the interval and price specified for the subscription. At each subscription renewal, Google Play charges the user account automatically, then notifies the user of the charges afterward by email. Billing cycles will always match subscription cycles, based on the purchase date.

Over the life of a subscription, the form of payment billed remains the same — Google Play always bills the same form of payment (such as credit card or by Direct Carrier Billing) that was originally used to purchase the subscription.

サブスクリプションの支払い間隔と価格設定は無期限で反映される. サブスクリプションが更新されたり, 自動請求されるとユーザにemailで通知される. サブスクリプションを購入した日付を基準に, 請求される

購読中の支払い方法(クレジットカードや直接支払など)は変わらない. Google Playはサブスクリプション購入時と同じ請求を常に行う.

When the subscription payment is approved by Google Wallet, Google Play provides a purchase token back to the purchasing app through the In-app Billing API. Your apps can store the token locally or pass it to your backend servers, which can then use it to validate or cancel the subscription remotely using the Purchase Status API.

Google Walletによりサブスクリプションの支払いが承認された時, In-app Billing APIを介してGoogle Playから購入Tokenが返される.
購入Tokenはローカルに保存するかバックエンドサーバに送信し, 外部からPurchase Status APIを使用してサブスクリプションを有効にしたり停止したりすることができる.

If a recurring payment fails (for example, because the customer’s credit card has become invalid), the subscription does not renew. How your app is notified depends on the In-app Billing API version that you are using:

  • With In-app Billing Version 3, the failed or expired subscription is no longer returned when you call getPurchases.
  • With In-app Billing Version 2, Google Play notifies your app at the end of the active cycle that the purchase state of the subscription is now “Expired”.

クレジットカードが失効される等して定期支払が失敗するとサブスクリプションは更新されなくなる.
これがどうやってアプリに通知されるかは使用しているIn-app Billing APIのバージョンに依存する.

  • Ver.3を使用している場合, 期限切れや失敗した場合はgetPurchasesは商品を返さない.
  • Ver.2を使用している場合, Google Playは購入状態が”Expired”になった時にアプリへ通知される.

Recommendation: Include business logic in your app to notify your backend servers of subscription purchases, tokens, and any billing errors that may occur. Your backend servers can use the server-side API to query and update your records and follow up with customers directly, if needed.

Recommendation:バックエンドサーバを使っているならサブスクリプションの購入, Token, 発生する可能性のあるエラーを通知するようビジネスロジックを組み込んでおく.
バックエンドサーバはサーバサイドAPIを使用して直接ユーザに問い合わせたり, 情報更新したり, 追跡することができる.

Free trials

In the Developer Console, you can set up a free trial period that lets users try your subscription content before buying it. The trial period runs for the period of time that you set and then automatically converts to a full subscription managed according to the subscription’s billing interval and price.

To take advantage of a free trial, a user must “purchase” the full subscription through the standard In-app Billing flow, providing a valid form of payment to use for billing and completing the normal purchase transaction. However, the user is not charged any money, since the initial period corresponds to the free trial. Instead, Google Play records a transaction of $0.00 and the subscription is marked as purchased for the duration of the trial period or until cancellation. When the transaction is complete, Google Play notifies users by email that they have purchased a subscription that includes a free trial period and that the initial charge was $0.00.

Developer Consoleではコンテンツ購入前に無料のお試し期間を設定できる. お試し期間が満了すると, 自動で有料のサブスクリプションに移行される.

お試し期間を利用するには, フルサブスクリプションを通常の購入フローを通して”購入”しなければならない.
ただし, 試用期間内であれば料金を請求されずGoogle Playは0ドルの取引を記録して試用期間であることをマークする.
取引が完了するとGoogle Playはユーザに購入済みのサブスリクプションがある旨とそれが無料期間中であり取引0ドルであることを通知する.

When the trial period ends, Google Play automatically initiates billing against the credit card that the user provided during the initial purchase, at the amount set for the full subscription, and continuing at the subscription interval. If necessary, the user can cancel the subscription at any time during the trial period. In this case, Google Play marks the subscription as expired immediately, rather than waiting until the end of the trial period. The user has not paid for the trial period and so is not entitled to continued access after cancellation.

お試し期間が終わるとフルサブスクリプションが購入されたとして改めて自動で請求を行う.
必要であればお試し期間をキャンセルすることができるが, キャンセルするとお試し期間は満了となる.
お試し期間にキャンセルすると, それ以降アクセスさせなくする.

You can set up a trial period for a subscription in the Developer Console, without needing to modify or update your APK. Just locate and edit the subscription in your product list, set a valid number of days for the trial (must be 7 days or longer), and publish. You can change the period any time, although note that Google Play does not apply the change to users who have already “purchased” a trial period for the subscription. Only new subscription purchases will use the updated trial period. You can create one free trial period per subscription product.

お試し期間はAPKを更新することなくDeveloper Consoleで設定できる.
商品リストから対象のサブスクリプションを見つけ出し, お試し期間となる日数(期間は7日かそれ以上とすること)を入力し公開する. お試し期間はいつでも変更できる. ただし, すでに購入されているものに対しては変更が適用されない.

Subscription publishing

When you have finished configuring your subscription product details in the Developer Console, you can publish the subscription in the app product list.

Developer Consoleでサブスクリプションの設定が完了したら商品リストに公開できる.

In the product list, you can add subscriptions, in-app products, or both. You can add multiple subscriptions that give access to different content or services, or you can add multiple subscriptions that give access to the same content but for different intervals or different prices, such as for a promotion. For example, a news outlet might decide to offer both monthly and annual subscriptions to the same content, with annual having a discount. You can also offer in-app purchase equivalents for subscription products, to ensure that your content is available to users of older devices that do not support subscriptions.

プロダクトリストにはサブスクリプションかアプリ内商品, または両方を追加できる. 異なるコンテンツまたはサービスとして複数のサブスクリプションを追加するもよし, 同じコンテンツでも異なる価格と支払い間隔を持たせるもよし.
たとえばプロモーションのために, 月額制と年額制の2本を用意して年額の方が割引率を高くするような場合に使える.
また, サブスクリプションをサポートしない古い端末向けにアプリ内課金を使って通常商品を代用することもある.

After you add a subscription or in-app product to the product list, you must publish the product before Google Play can make it available for purchase. Note that you must also publish the app itself before Google Play will make the products available for purchase inside the app.

Important: You can remove the subscription product from the product list offered in your app to prevent users from seeing or purchasing it.

サブスクリプションかアプリ内商品を商品リストに追加した後, Google Playで購入できるようになる前にそれを公開する必要がある.
ただ, アプリ内商品を作成するには事前にアプリを登録しておく必要がある.

Important:サブスクリプションを見えなくしたり購入できなくしたりするために, プロダクトリストからサブスクリプションを削除することができる.

Subscription Cancellation

Users can view the status of all of their subscriptions and cancel them if necessary from the My Apps screen in the Play Store app. Currently, the In-app Billing API does not provide support for programatically canceling subscriptions from inside the purchasing app.

ユーザはPley Storeにあるマイアプリ画面からすべてのサブスクリプションを閲覧, キャンセルできる.
現在, In-app Billing APIはアプリからプログラマブルにサブスクリプションを解除する手段を備えていない.

When the user cancels a subscription, Google Play does not offer a refund for the current billing cycle. Instead, it allows the user to have access to the cancelled subscription until the end of the current billing cycle, at which time it terminates the subscription. For example, if a user purchases a monthly subscription and cancels it on the 15th day of the cycle, Google Play will consider the subscription valid until the end of the 30th day (or other day, depending on the month).

ユーザがサブスクリプションを解除しても, Google Playは払い戻し請求をサポートしていない. 代わりに, 支払ったサブスクリプション期間が満了するまでキャンセル後でもコンテンツにアクセスできる.
(たとえば15日に購読停止しても30日まではアクセスできる)

In some cases, the user may contact you directly to request cancellation of a subscription. In this and similar cases, you can use the server-side API to query and directly cancel the user’s subscription from your servers.

他には販売者に購読停止のリクエストを直接連絡する方法やサーバサイドAPIを使用してサーバから直接購読停止させることもできる

Important: In all cases, you must continue to offer the content that your subscribers have purchased through their subscriptions, for as long any users are able to access it. That is, you must not remove any subscriber’s content while any user still has an active subscription to it, even if that subscription will terminate at the end of the current billing cycle. Removing content that a subscriber is entitled to access will result in penalties. Please see the policies document for more information.

Important:全てのケースにおいて, サブスクリプションを購入したユーザがアクセスし得る限りコンテンツを提供し続けること.
コンテンツの削除はアクセス権をもったユーザへペナルティを科すことになる. より多くの情報はpolicies documentを参照.

App uninstallation

When the user uninstalls an app that includes purchased subscriptions, the Play Store app will notify the user that there are active subscriptions. If the user chooses to continue with the uninstallation, the app is removed and the subscriptions remain active and recurring billing continues. The user can return to cancel the associated subscriptions at any time in the My Apps screen of the Play Store app. If the user chooses to cancel the uninstallation, the app and subscriptions remain as they were.

購入済みのサブスクリプションを含んだアプリをアンインストールした場合は, Play Store アプリはユーザに有効なサブスクリプションの存在を伝える. それでもユーザがアンインストールを継続した場合, アプリは削除されサブスクリプションはアクティブなまま残り, 定期支払も継続される.
この後, サブスクリプションを停止したい場合はPlay Storeの”マイアプリ”から行う. アンインストールをキャンセルした場合はアプリもサブスクリプションもそのままの状態で残る.

Refunds

With subscriptions, Google Play does not provide a refund window, so users will need to contact you directly to request a refund.

If you receive requests for refunds, you can use the server-side API to cancel the subscription or verify that it is already cancelled. However, keep in mind that Google Play considers cancelled subscriptions valid until the end of their current billing cycles, so even if you grant a refund and cancel the subscription, the user will still have access to the content.

Important: Partial refunds for canceled subscriptions are not available at this time.

Google Playは払い戻し機能と画面を提供していないので, 必要であれば直接販売者に連絡する必要がある.

払い戻しのリクエストを受けた場合は, サーバサイドAPIを使ってサブスクリプションを停止することができる.
しかし, Google Playは購読停止しても一定期間はユーザにアクセスを許していることに留意すること.

Importantサブスクリプションの部分的な払い戻しは現時点でサポートされない.

Payment Processing and Policies

In general, the terms of Google Play allow you to sell in-app subscriptions only through the standard payment processor, Google Wallet. For purchases of any subscription products, the transaction fee is the same as the transaction fee for application purchases (30%).

サブスクリプションはGoogle Walletを通して販売することができる. サブスクリプション購入の手数料はアプリ販売と同じ30%となる.

Apps published on Google Play that are selling subscriptions must use In-app Billing to handle the transaction and may not provide links to a purchase flow outside of the app and Google Play (such as to a web site).

For complete details about terms and policies, see the policies document.

Google Playでアプリを公開してサブスクリプションを販売したならば, In-app Billingを使用すること.
Google Playの外で購入させることはできない.

Subscription order numbers

To help you track transactions relating to a given subscription, Google Wallet provides a base Merchant Order Number for all recurrences of the subscription and denotes each recurring transaction by appending an integer as follows:
12999556515565155651.5565135565155651 (base order number)
12999556515565155651.5565135565155651..0 (initial purchase orderID)
12999556515565155651.5565135565155651..1 (first recurrence orderID)
12999556515565155651.5565135565155651..2 (second recurrence orderID)

Google Walletは全てのサブスクリプションに販売者オーダ番号にインクリメンタルIDを付与して取引の追跡を助けます.
12999556515565155651.5565135565155651 (base order number)
12999556515565155651.5565135565155651..0 (initial purchase orderID)
12999556515565155651.5565135565155651..1 (first recurrence orderID)
12999556515565155651.5565135565155651..2 (second recurrence orderID)

Google Play provides the order number as the value of the orderId field of the INAPP_PURCHASE_DATA JSON field (in V3) or the PURCHASE_STATE_CHANGED intent (in V2).

Google Playが提供するオーダ番号をJSONのIN_APP_PURCHASE_DATAにあるorderIdフィールドから取得できる(Ver.3), Ver.2の場合はIntentのPURCHASE_STATE_CHANGEDとなる.

Purchase Verification Strategies

In a typical scenario, your app verifies the order status for new purchases to ensure that they are valid before granting access to the purchased content.

To verify a purchase, the app passes the purchase token and other details up to your backend servers, which verifies them directly with Google Play using the Purchase Status API. If the backend server determines that the purchase is valid, it notifies the app and grants access to the content.

Keep in mind that users will want the ability to use your app at any time, including when there may be no network connection available. Make sure that your approach to purchase verification accounts for the offline use-case.

通常, コンテンツへのアクセス権を付与する前に, 購入商品の注文内容を検証する.

注文を検証するために, 購入Tokenとその他の詳細な情報をバックエンドサーバに送り, Purchase Status APIを使ってGoogle Playへ直接アクセスする.

忘れてはいけないことに, ユーザはネットワーク接続できない場合がある.
ネットワーク接続ができない状態でもコンテンツが利用できるよう配慮すること.
オフラインでのユースケースを確認すること.

Google Play Android Developer API

Google Play offers an HTTP-based API that lets you remotely query the validity of a specific subscription at any time or cancel a subscription. The API is designed to be used from your backend servers as a way of securely managing subscriptions, as well as extending and integrating subscriptions with other services.

For complete information, see Purchase Status API.

Google Playはサブスクリプションをいつでも検証あるいはキャンセルできるようHTTP-baseのAPIを提供している.
APIはバックエンドサーバから使用されることを想定して安全に設計されている.

より詳しい情報はPurchase Status APIを参照.


License:
Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.

Android:Selling In-app Products

$
0
0

Introduction

AndroidではGoogle Playサービスを利用してアプリ内課金を比較的容易に導入できます.
本稿ではビジネス戦略の1つ”アプリ内課金”にフォーカスしたAndroid Developers | Selling In-app Productsを翻訳し, その実装方法について学びます.

Selling In-app Products

In this class, you’ll learn how to perform common In-app Billing operations from Android applications.

本章では, アプリ内課金に関する共通のオペレーションについて記載する.

In-app billing is a service hosted on Google Play that lets you charge for digital content or for upgrades in your app. The In-app Billing API makes it easy for you to integrate In-app Billing into your applications. You can request product details from Google Play, issue orders for in-app products, and quickly retrieve ownership information based on users’ purchase history. You can also query the Google Play service for details about in-app products, such as local pricing and availability.

アプリ内課金はデジタルコンテンツやアプリの有料アップグレードの請求に利用できるGoogle Playのサービスである.
アプリ内課金APIは簡単にアプリケーションに組み込むことができる.
Google Playに商品の詳細をリクエストしたり, アプリ内商品のオーダを発行したり,
ユーザのアイテム購入履歴から所有者情報を復元したりもできる.
そして, アプリ内商品のローカル価格や有効性についての詳細をGoogle Playに問い合わせできる.

Google Play provides a checkout interface that makes user interactions with the In-app Billing service seamless, and provides a more intuitive experience to your users.

Google Playはシームレスな清算インタフェースをアプリ内課金サービスで提供し, より直感的なユーザ体験を提供する.

This class describes how to get started with the Version 3 API. To learn how to use the version 2 API, see Implementing In-App Billing (V2).

この投稿ではIn-app Billing APIのVer.3について記載する. Ver.2のAPIについて知りたい場合はImplementing In-App Billing (V2).を参照.

Preparing Your In-app Billing Application

Before you can start using the In-app Billing service, you’ll need to add the library that contains the In-app Billing Version 3 API to your Android project. You also need to set the permissions for your application to communicate with Google Play. In addition, you’ll need to establish a connection between your application and Google Play. You should also verify that the In-app Billing API version that you are using in your application is supported by Google Play.

アプリ内課金サービスを利用するためにはAndroidプロジェクトへIn-app Billing Ver.3 APIし,
Google Playと通信するためにパーミッションの定義を必要とする.
さらに, 使用するアプリ内課金のAPIバージョンが, 連携するGoogle Playでサポートされているかを確認する必要がある.

Download the Sample Application

In this training class, you will use a reference implementation for the In-app Billing Version 3 API called the TrivialDrive sample application. The sample includes convenience classes to quickly set up the In-app Billing service, marshal and unmarshal data types, and handle In-app Billing requests from the main thread of your application.

ここでは, In-app Billing API Ver.3 を使用するTrivialDriveサンプルアプリを例に実装方法を見る.
サンプルアプリは, In-app Billing serviceのセットアップがすぐにでき, 組織化および非組織化されたデータ種別, そしてアプリmainスレッドでIn-app Billingリクエストをハンドルする便利クラスを含んでいる.

To download the sample application:

  • Open the Android SDK Manager.
  • In the SDK Manager, expand the Extras section.
  • Select Google Play Billing Library.
  • Click Install packages to complete the download.

The sample files will be installed to <sdk>/extras/google/play_billing/.

サンプルアプリのダウンロード方法は次の通り.

  1. Android SDK Managerを起動
  2. SDK ManagerでExtrasセクションを展開
  3. Google Play Billing Libraryを選択
  4. Install packagesを選択してダウンロードする

サンプルは<sdk>/extras/google/play_billing/にダウンロードされる.

Add Your Application to the Developer Console

The Google Play Developer Console is where you publish your In-app Billing application and manage the various digital goods that are available for purchase from your application. When you create a new application entry in the Developer Console, it automatically generates a public license key for your application.

Google Play Developer Consoleはアプリ内課金アプリを公開し, アプリで購入可能な様々な商品の管理も行う.
Developer Consoleで新規にアプリ登録した場合は, 自動でパブリックライセンスキーが払い出される.

You will need this key to establish a trusted connection from your application to the Google Play servers. You only need to generate this key once per application, and don’t need to repeat these steps when you update the APK file for your application.

パブリックライセンスキーは, アプリとGoogle Play間でセキュア接続を確立するために必要になる.
このキーを生成するのは一度だけで. アプリアップデートの度に毎回生成し直す必要はない.

To add your application to the Developer Console:

  1. Go to the Google Play Developer Console site and log in. You will need to register for a new developer account, if you have not registered previously. To sell in-app items, you also need to have a Google Wallet merchant account.
  2. Click on Try the new design to access the preview version of the Developer Console, if you are not already logged on to that version.
  3. In the All Applications tab, add a new application entry.
    • Click Add new application.
    • Enter a name for your new In-app Billing application.
    • Click Prepare Store Listing.
  4. In the Services & APIs tab, find and make a note of the public license key that Google Play generated for your application. This is a Base64 string that you will need to include in your application code later.

Your application should now appear in the list of applications in Developer Console.

Developer Consoleにアプリを追加する:

  1. Google Play Developer Consoleにログインする. 開発用アカウントを持っていない場合は作成する.
    アプリ内商品を売るにはGoogle Wallet merchantアカウントが必要になる.
  2. プレビューバージョンのDeveloper Consoleにルグインしていない場合はTry the new designをクリック
  3. All Applicationタブから新しくアプリを登録
    - Add new applicationをクリック
    - In-app Billingアプリの名前を入力
    - Prepare Store Listingをクリック
  4. Service & APIsタブで, 生成されたパブリックライセンスキーをメモする.
    キーはBase64エンコードされた文字列で, 後でアプリに組み込む必要がある.

これでアプリがDeveloper Consoleのアプリ一覧に追加される.

Add the In-app Billing Library

To use the In-app Billing Version 3 features, you must add the IInAppBillingService.aidl file to your Android project. This Android Interface Definition Language (AIDL) file defines the interface to the Google Play service.

In-app Builling Ver.3 APIを使うためには, Google Playサービスのインターフェースを定義したIInAppBillingService.aidlをAndroidプロジェクトへ追加する.

You can find the IInAppBillingService.aidl file in the provided sample app. Depending on whether you are creating a new application or modifying an existing application, follow the instructions below to add the In-app Billing Library to your project.

サンプルアプリもIInAppBillingService.aidlを持っている.
In-app Billing Version 3 library追加の手順を, 新規アプリ作成する場合 or 既存アプリを変更する場合 それぞれで例示する.

New Project

To add the In-app Billing Version 3 library to your new In-app Billing project:

新規アプリ作成する場合:

  1. Copy the TrivialDrive sample files into your Android project.
  2. Modify the package name in the files you copied to use the package name for your project. In Eclipse, you can use this shortcut: right-click the package name, then select Refactor > Rename.
  3. Open the AndroidManifest.xml file and update the package attribute value to use the package name for your project.
  4. Fix import statements as needed so that your project compiles correctly. In Eclipse, you can use this shortcut: press Ctrl+Shift+O in each file showing errors.
  5. Modify the sample to create your own application. Remember to copy the Base64 public license key for your application from the Developer Console over to your MainActivity.java.
  1. TrivialDriveサンプルにあるファイルをプロジェクトにコピーする
  2. ファイル内のパッケージ名を自アプリのパッケージ名に置き換える
  3. AndroidManifest.xmlのパッケージ名をあなたのアプリのパッケージ名に置き換える
  4. 必要なクラスをimportし, エラーをとる
  5. MainActivity.javaに, Developer Consoleで払い出されたBase64パブリックライセンスキーに置き換える
Existing Project

To add the In-app Billing Version 3 library to your existing In-app Billing project:

既存プロジェクトに追加する場合:

  1. Copy the IInAppBillingService.aidl file to your Android project.
    • If you are using Eclipse: Import the IInAppBillingService.aidl file into your /src directory.
    • If you are developing in a non-Eclipse environment: Create the following directory /src/com/android/vending/billing and copy the IInAppBillingService.aidl file into this directory.
  2. Build your application. You should see a generated file named IInAppBillingService.java in the /gen directory of your project.
  3. Add the helper classes from the /util directory of the TrivialDrive sample to your project. Remember to change the package name declarations in those files accordingly so that your project compiles correctly.
  1. IInAppBillingService.aidlファイルをプロジェクトにコピーする
  2. プロジェクトをビルドし, /genに生成されたIInAppBillingService.javaファイルを確認する
  3. TriviaDriveサンプルの/utilディレクトリにあるヘルパークラスをプロジェクトにコピーしてパッケージ名を変更する(パッケージ名は適宜変更する)

Your project should now contain the In-app Billing Version 3 library.

これでアプリにIn-app Billing Ver.3 libraryが取り込まれる.

Set the Billing Permission

Your app needs to have permission to communicate request and response messages to the Google Play’s billing service. To give your app the necessary permission, add this line in your AndroidManifest.xml manifest file:

Google Playの課金Serviceと通信する為に必要なPermissionをAndroidManifest.xmlに定義する.

<uses-permissionandroid:name="com.android.vending.BILLING" />

Initiate a Connection with Google Play

You must bind your Activity to Google Play’s In-app Billing service to send In-app Billing requests to Google Play from your application. The convenience classes provided in the sample handles the binding to the In-app Billing service, so you don’t have to manage the network connection directly.

アプリからGoogle Playへ課金リクエストを送るには, ActivityをGoogle Playの課金Serviceにバインドする必要がある.
サンプルにあるヘルパークラスが課金Serviceへのバインドを処理するため, ネットワーク接続をあなたが直接意識する必要はない.

To set up synchronous communication with Google Play, create an IabHelper instance in your activity’s onCreate method. In the constructor, pass in the Context for the activity, along with a string containing the public license key that was generated earlier by the Google Play Developer Console.

Google Playとの同期通信を準備するため, ActivityonCreateメソッドでIabHelperインスタンスを生成する.
コンストラクタにはContextとパブリックライセンスキーを渡す.

Security Recommendation: It is highly recommended that you do not hard-code the exact public license key string value as provided by Google Play. Instead, you can construct the whole public license key string at runtime from substrings, or retrieve it from an encrypted store, before passing it to the constructor. This approach makes it more difficult for malicious third-parties to modify the public license key string in your APK file.

Security Recommendation:
パブリックライセンスキーは実行時に部分文字列から構築して生成するか, 暗号化して管理し, ハードコーディングは避けるように.
これは悪意のある第三者によるAPKにあるライセンスキーの改竄をより困難にする.

IabHelper mHelper;

@Override
publicvoidonCreate(Bundle savedInstanceState) {
// ...
String base64EncodedPublicKey;

// compute your public key and store it in base64EncodedPublicKey
mHelper = new IabHelper(this, base64EncodedPublicKey);
}

Next, perform the service binding by calling the startSetup method on the IabHelper instance that you created. Pass the method an OnIabSetupFinishedListener instance, which is called once the IabHelper completes the asynchronous setup operation. As part of the setup process, the IabHelper also checks if the In-app Billing Version 3 API is supported by Google Play. If the API version is not supported, or if an error occured while establishing the service binding, the listener is notified and passed an IabResult object with the error message.

次に、IabHelperstartSetupメソッドを呼んでServiceをバインドする. 非同期のセットアップが完了するとOnIabSetupFinishedListenerのメソッドが呼ばれる.
セットアップではIabHelperが, Google PlayのIn-app Billing Ver.3サポートをチェックする.
もしAPIがサポートされていなかったり, サービスのバインドが失敗した場合には, エラーメッセージを含んだIabResultオブジェクトがリスナーに通知される.

mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
publicvoidonIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log.d(TAG, "Problem setting up In-app Billing: " + result);
}
// Hooray, IAB is fully set up!
}
});

If the setup completed successfully, you can now use the mHelper reference to communicate with the Google Play service. When your application is launched, it is a good practice to query Google Play to find out what in-app items are owned by a user. This is covered further in the Query Purchased Items section.

セットアップが正常に完了したらGoogle Play Serviceと通信するmHelperが使用できる.
アプリ起動時にユーザの商品所有状態をチェックするのが良い方法である.
これについてはQuery Purchased Itemsで述べる.

Important: Remember to unbind from the In-app Billing service when you are done with your activity. If you don’t unbind, the open service connection could cause your device’s performance to degrade. To unbind and free your system resources, call the IabHelper’s dispose method when your Activity gets destroyed.

重要: Activity終了時に課金Serviceをアンバインドすること.
アンバインドを忘れてサービスとのコネクションを接続したままにすると端末のパフォーマンスが悪くなる.
アンバインドとシステムリソースを解放するにはIabHelperdisposeメソッドをActivity破棄時に呼び出すこと.

@Override
publicvoidonDestroy() {
super.onDestroy();
if (mHelper != null) mHelper.dispose();
mHelper = null;
}

Establishing In-app Billing Products for Sale

Before publishing your In-app Billing application, you’ll need to define the product list of digital goods available for purchase in the Google Play Developer Console.

課金アプリを公開する前に, Google Play Developer Consoleで販売できる商品のプロダクトリストを定義する.

Specify In-app Products in Google Play

From the Developer Console, you can define product information for in-app products and associate the product list with your application.

Developer Consoleで, アプリ内商品を定義してアプリと関連付ける.

To add new in-app products to your product list:

新しいin-appプロダクトをリストに追加する方法:

  1. Build a signed APK file for your In-app Billing application. To learn how to build and sign your APK, see Building Your Application for Release. Make sure that you are using your final (not debug) certificate and private key to sign your application.
  2. In the Developer Console, open the application entry that you created earlier.
  3. Click on the APK tab then click on Upload new APK. Upload the signed APK file to the Developer Console. Don’t publish the app yet!
  4. Navigate to the uploaded app listing, and click on In-app Products.
  5. Click on the option to add a new product, then complete the form to specify the product information such as the item’s unique product ID (also called its SKU), description, price, and country availability. Note down the product ID since you might need this information to query purchase details in your application later.
    Important: The In-app Billing Version 3 service only supports managed in-app products, so make sure that you specify that the purchase type is ‘Managed’ when you add new items to your product list in the Developer Console.
  6. Once you have completed the form, activate the product so that your application can purchase it.
    Warning: It may take up to 2-3 hours after uploading the APK for Google Play to recognize your updated APK version. If you try to test your application before your uploaded APK is recognized by Google Play, your application will receive a ‘purchase cancelled’ response with an error message “This version of the application is not enabled for In-app Billing.”
  1. 商用署名済みAPKを作成する. 商用署名ビルドについてはBuilding Your Application for Releaseを参照.
  2. Developer Consoleでアプリの登録画面を開く
  3. APKタブ > Upload new APKと進み, 署名済みAPKをアップロードする(まだ公開しないこと!)
  4. アップロードしたアプリのIn-app Productをクリック.
  5. 新しい商品を追加するためのoptionをクリック.
    商品情報(アイテムのユニークID(SKU), 説明, 価格, 利用できる国)をフォームに入力する. ユニークIDはこのあとのアプリでの購入情報の問い合わせに必要になるため書き留めておくこと.
    重要:
    In-app Billing Ver.3サービスは管理されたアプリ内商品のみをサポートする.
    Developer Consoleで新しい商品をプロダクトリストに追加した際には, 購入種別が”Managed”に指定されていることを確認すること.
  6. 登録が終わったら商品をアプリで購入することが可能になる.
    注意:
    APKはGoogle Playにアップしてから2-3時間後に有効化される場合がある.
    もし反映されるよりも前にテストすると, “purcase cancelled”, “This version of the application is not enabled for In-app Billing”のエラーが応答される.

Query Items Available for Purchase

You can query Google Play to programmatically retrieve details of the in-app products that are associated with your application (such as the product’s price, title, description, and type). This is useful, for example, when you want to display a listing of unowned items that are still available for purchase to users.

アプリと関係する商品情報(商品価格, タイトル, 説明, 種別)をプログラマブルにGoogle Playへ問い合わせることができる.
例えば, 未購入の商品を表示したい場合などに使用される.

Note: When making the query, you will need to specify the product IDs for the products explicitly. You can manually find the product IDs from the Developer Console by opening the In-app Products tab for your application. The product IDs are listed under the column labeled Name/ID.

Note:クエリにはどの商品かを特定するための商品IDが必要になる.
商品IDはDeveloper ConsoleのIn-app ProductsタブにあるName/IDラベルのカラムにリストされている.

To retrieve the product details, call queryInventoryAsync(boolean, List, QueryInventoryFinishedListener) on your IabHelper instance.

商品を検索するのに, IabHelperqueryInventoryAsync(boolean, List, QueryInventoryFinishedListener)メソッドが使える.

  1. The first input argument indicates whether product details should be retrieved (should be set to true).
  2. The List argument consists of one or more product IDs (also called SKUs) for the products that you want to query.
  3. Finally, the QueryInventoryFinishedListener argument specifies a listener is notified when the query operation has completed and handles the query response.
  1. 1つめの引数は商品情報を検索対象とするかどうか(検索する場合はtrueをセット)
  2. 引数Listは, 検索対象の商品ID(SKU)を指定
  3. 引数QueryInventoryFinishedListenerは, クエリ完了とその結果を受け取るためのリスナを指定する

If you use the convenience classes provided in the sample, the classes will handle background thread management for In-app Billing requests, so you can safely make queries from the main thread of your application.

もしサンプルにあるヘルパークラスを使用すれば, 課金リクエストをバックグラウンドで実行するのでメインスレッドからでも安全に呼び出せる.

The following code shows how you can retrieve the details for two products with IDs SKU_APPLE and SKU_BANANA that you previously defined in the Developer Console.

次はDeveloper Consoleであらかじめ定義したSKU_APPLESKU_BANANA, 2つの商品詳細を検索するコード.

List additionalSkuList = new List();
additionalSkuList.add(SKU_APPLE);
additionalSkuList.add(SKU_BANANA);
mHelper.queryInventoryAsync(true, additionalSkuList,
mQueryFinishedListener);

If the query is successful, the query results are stored in an Inventory object that is passed back to the listener.

クエリが成功したらInventoryオブジェクトに結果が格納されてリスナーに通知される.

The following code shows how you can retrieve the item prices from the result set.

次のコードは結果セットから商品価格を取り出す方法.

IabHelper.QueryInventoryFinishedListener 
mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
publicvoidonQueryInventoryFinished(IabResult result, Inventory inventory)
{
if (result.isFailure()) {
// handle error
return;
}

String applePrice =
inventory.getSkuDetails(SKU_APPLE).getPrice();
String bananaPrice =
inventory.getSkuDetails(SKU_BANANA).getPrice();

// update the UI
}
}

Purchasing In-app Billing Products

Once your application is connected to Google Play, you can initiate purchase requests for in-app products. Google Play provides a checkout interface for users to enter their payment method, so your application does not need to handle payment transactions directly.

アプリとGoogle Playを接続すれば, アプリ内商品の購入リクエストを開始できる.
Google Playは支払い方法を指定できるチェックアウト用のインタフェースを提供するため, アプリは直接支払い処理を実装する必要がない.

When an item is purchased, Google Play recognizes that the user has ownership of that item and prevents the user from purchasing another item with the same product ID until it is consumed. You can control how the item is consumed in your application, and notify Google Play to make the item available for purchase again.

商品の支払いが完了した時, Google Playはユーザがその商品を所有したことを認識し, 同じIDの商品を購入できないようケアする.
アプリ内商品の消費はコントロールでき, 再び購入可能とするためにGoogle Playへ通知することができる.

You can also query Google Play to quickly retrieve the list of purchases that were made by the user. This is useful, for example, when you want to restore the user’s purchases when your user launches your app.

また, Google Playにユーザの購入履歴を問い合わせることができる.
例えば, アプリ起動時に購入履歴から商品の所有状態を復元するのに利用できる.

Purchase an Item

To start a purchase request from your app, call launchPurchaseFlow(Activity, String, int, OnIabPurchaseFinishedListener, String) on your IabHelper instance. You must make this call from the main thread of your Activity. Here’s an explaination of the launchPurchaseFlow method parameters:

アプリから購入リクエストを開始する時, IabHelperlaunchPurchaseFlow(Activity, String, int, OnIabPurchaseFinishedListener, String)を呼び出す. これはActivityのメインスレッドから呼ぶこと. launchPurchaseFlowメソッドのパラメータは下記の通り.

  • The first argument is the calling Activity.
  • The second argument is the product ID (also called its SKU) of the item to purchase. Make sure that you are providing the ID and not the product name. You must have previously defined and activated the item in the Developer Console, otherwise it won’t be recognized.
  • The third argument is a request code value. This value can be any positive integer. Google Play returns this request code to the calling Activity’s onActivityResult along with the purchase response.
  • The fourth argument is a listener that is notified when the purchase operation has completed and handles the purchase response from Google Play.
  • The fifth argument contains a ‘developer payload’ string that you can use to send supplemental information about an order (it can be an empty string). Typically, this is used to pass in a string token that uniquely identifies this purchase request. If you specify a string value, Google Play returns this string along with the purchase response. Subsequently, when you make queries about this purchase, Google Play returns this string together with the purchase details.
    Security Recommendation: It’s good practice to pass in a string that helps your application to identify the user who made the purchase, so that you can later verify that this is a legitimate purchase by that user. For consumable items, you can use a randomly generated string, but for non-consumable items you should use a string that uniquely identifies the user.
  • 第一引数は呼び出し元のActivity
  • 第二引数は購入アイテムの商品ID(SKU). 商品名ではなく商品IDとすること.
    商品は事前にDeveloper Consoleで定義しておかないと商品として認識されない.
  • 第三引数はリクエストコード. この値は正の数となる.
    Google Playは購入リクエストの応答(ActivityonActivityResult)で, このリクエストコードを返す
  • 第四引数は購入操作が完了し, Google Playからの応答をハンドルするリスナー
  • 第五引数は任意のヘッダ文字列(‘developer payload’), 注文の付加情報として使用できる(空文字でもよい)
    通常, 購入リクエストを識別するためのユニークID(token)が使用される.
    Google Playの購入レスポンスには, ここで指定した文字列が一緒に渡される.
    続いて, この購入情報を問い合わせる時, Google Playは購入情報の詳細と一緒にこの文字列を返す.
    Security Recommendation:
    この方法は誰の購入情報かを識別するのに良い方法となる.
    これにより, 後から商品購入の妥当性をチェックできる. 消耗品にはランダムなtokenを使い回すだろうが,
    永続的な商品の場合はユニークIDを使用すべきである.

The following example shows how you can make a purchase request for a product with ID SKU_GAS, using an arbitrary value of 10001 for the request code, and an encoded developer payload string.

次の例は, 商品IDがSKU_GAS, 任意のリクエストコード(10001), エンコードしたdeveloper payload文字列を指定したの商品購入リクエストを使用する方法である.

mHelper.launchPurchaseFlow(this, SKU_GAS, 10001,   
mPurchaseFinishedListener, "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");

If the purchase order is successful, the response data from Google Play is stored in an Purchase object that is passed back to the listener.

もし購入オーダが成功すれば, Google Playからの応答データはPurchaseオブジェクトに格納されてリスナーに通知される.

The following example shows how you can handle the purchase response in the listener, depending on whether the purchase order was completed successfully, and whether the user purchased gas or a premium upgrade. In this example, gas is an in-app product that can be purchased multiple times, so you should consume the purchase to allow the user to buy it again. To learn how to consume purchases, see the [Consuming Products][t1_3c] section. The premium upgrade is a one-time purchase so you don’t need to consume it. It is good practice to update the UI immediately so that your users can see their newly purchased items.

次はリスナーで購入レスポンスをどのようにして処理するかを例示する.
レスポンスから購入した商品がgasなのかプレミアムアップグレードなのかで処理を分けている.
サンプルコードで, gasは複数回購入可能な商品であり, 再度購入できるよう消費させている.
どうやって購入した商品を消費させるかはConsuming Productsセクションで述べる.
プレミアムアップグレードは一度だけ購入できるアイテムであるため消費させない.
ユーザが購入できたことを認識できるよう, すぐにUIを更新するのが良い方法である.

IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener 
= new IabHelper.OnIabPurchaseFinishedListener() {
publicvoidonIabPurchaseFinished(IabResult result, Purchase purchase)
{
if (result.isFailure()) {
Log.d(TAG, "Error purchasing: " + result);
return;
}
elseif (purchase.getSku().equals(SKU_GAS)) {
// consume the gas and update the UI
}
elseif (purchase.getSku().equals(SKU_PREMIUM)) {
// give user access to premium content and update the UI
}
}
};

Security Recommendation: When you receive the purchase response from Google Play, make sure to check the returned data signature, the orderId, and the developerPayload string in the Purchase object to make sure that you are getting the expected values. You should verify that the orderId is a unique value that you have not previously processed, and the developerPayload string matches the token that you sent previously with the purchase request. As a further security precaution, you should perform the verification on your own secure server.

Security Recommendation: Google Playから支払いの応答を受けたとき, 返却されたPurchaseのsignature, orderId, developerPayloadが期待する値かチェックする(Man in the middle attackの防止). orderIdが過去に処理していないユニークな値であるか, developerPayloadtokenが購入リクエスト時のものと一致するか. 更に厳重に信頼できる自前のセキュアサーバであることが望ましい.

Query Purchased Items

Upon a successful purchase, the user’s purchase data is cached locally by Google Play’s In-app Billing service. It is good practice to frequently query the In-app Billing service for the user’s purchases, for example whenever the app starts up or resumes, so that the user’s current in-app product ownership information is always reflected in your app.

購入が成功すると, そのデータはGoogle Playのアプリ内課金サービスにローカルキャッシュされる.
アプリの開始時, 再開時にユーザ購入履歴をアプリ内課金サービスに頻繁に問い合わせすることは, アプリ内商品の所有情報を常に最新に保つのによい方法となる.

To retrieve the user’s purchases from your app, call queryInventoryAsync(QueryInventoryFinishedListener) on your IabHelper instance. The QueryInventoryFinishedListener argument specifies a listener that is notified when the query operation has completed and handles the query response. It is safe to make this call fom your main thread.

アプリからユーザ購入情報を復元するために, IabHelperqueryInventoryAsync(QueryInventoryFinishedListener)が使用できる. 引数QueryInventoryFinishedListenerはクエリ完了時に結果通知を受け取るリスナを指定する.
このメソッドはメインスレッドから呼び出すことができる.

mHelper.queryInventoryAsync(mGotInventoryListener);

If the query is successful, the query results are stored in an Inventory object that is passed back to the listener. The In-app Billing service returns only the purchases made by the user account that is currently logged in to the device.

クエリが成功したとき, Inventoryオブジェクトに結果が格納されリスナーに通知される.
アプリ内課金サービスは現在端末でログインされているユーザアカウントの購入のみを返却する.

IabHelper.QueryInventoryFinishedListener mGotInventoryListener 
= new IabHelper.QueryInventoryFinishedListener() {
publicvoidonQueryInventoryFinished(IabResult result,
Inventory inventory) {

if (result.isFailure()) {
// handle error here
}
else {
// does the user have the premium upgrade?
mIsPremium = inventory.hasPurchase(SKU_PREMIUM);
// update UI accordingly
}
}
};

Consume a Purchase

You can use the In-app Billing Version 3 API to track the ownership of purchased items in Google Play. Once an item is purchased, it is considered to be “owned” and cannot be purchased again from Google Play while in that state. You must send a consumption request for the item before Google Play makes it available for purchase again. All managed in-app products are consumable. How you use the consumption mechanism in your app is up to you. Typically, you would implement consumption for products with temporary benefits that users may want to purchase multiple times (for example, in-game currency or replenishable game tokens). You would typically not want to implement consumption for products that are purchased once and provide a permanent effect (for example, a premium upgrade).

In-app Billing Ver.3 APIはユーザがGoogle Playで購入した商品の所有情報を追跡するために使える.
商品を購入すると”owned(所有中)”とみなされ, この状態ではGoogle Playで再購入できなくなる.
再購入できるようにするには商品の消費リクエストをGoogle Playに送信しなくてはならない(Purchase(購入)とConsume(消費)は対の概念).
アプリ内商品は全て消費可能であるが, 消費メカニズムをどう使うかはアプリ次第となる.
通常, 一時的に役立つような商品(アプリ内通貨や補給物資)には消費を実装すべきで,
永続的な効果を持つ商品の場合(プレミアムアップグレードなど)は一度限りの購入で完結し, 消費は実装しない.

It’s your responsibility to control and track how the in-app product is provisioned to the user. For example, if the user purchased in-game currency, you should update the player’s inventory with the amount of currency purchased.

アプリ内商品をどう管理し, どう追跡してユーザに提供するかはアプリの責務である.
例えば, ユーザがゲーム内通貨を購入したとき, プレイヤーの資金にそれを追加すべきである.

Security Recommendation: You must send a consumption request before provisioning the benefit of the consumable in-app purchase to the user. Make sure that you have received a successful consumption response from Google Play before you provision the item.

Security Recommendation:
商品の消費を反映する前に, 消費リクエストを送信すべきである.
商品を提供する前に消費成功の応答をGoogle Playから受け取るように.

To record a purchase consumption, call consumeAsync(Purchase, OnConsumeFinishedListener) on your IabHelper instance. The first argument that the method takes is the Purchase object representing the item to consume. The second argument is a OnConsumeFinishedListener that is notified when the consumption operation has completed and handles the consumption response from Google Play. It is safe to make this call fom your main thread.

購入消費を記録するにはIabHelperconsumeAsync(Purchase, OnConsumeFinishedListener)メソッドを呼ぶ.
第一引数は消費するアイテムのPurchaseオブジェクトを指定する. 第二引数は消費処理が完了しGoogle Playから応答の通知を受け取るOnConsumeFinishedListenerを指定する.
これはメインメソッドから呼び出せる.

In this example, you want to consume the gas item that the user has previously purchased in your app.

ユーザが購入した商品”gas”を消費するサンプル.

mHelper.consumeAsync(inventory.getPurchase(SKU_GAS), 
mConsumeFinishedListener);

The following example shows how to implement the OnConsumeFinishedListener.

OnConsumeFinishedListenerの実装サンプルは次の通り.

IabHelper.OnConsumeFinishedListener mConsumeFinishedListener =
new IabHelper.OnConsumeFinishedListener() {
publicvoidonConsumeFinished(Purchase purchase, IabResult result) {
if (result.isSuccess()) {
// provision the in-app purchase to the user
// (for example, credit 50 gold coins to player's character)
}
else {
// handle error
}
}
};

Check for Consumable Items on Startup

It’s important to check for consumable items when the user starts up your application. Typically, you would first query the In-app Billing service for the items purchased by the user (via queryInventoryAsync), then get the consumable Purchase objects from the Inventory. If your application detects that are any consumable items that are owned by the user, you should send a consumption request to Google Play immediately and provision the item to the user. See the TrivialDrive sample for an example of how to implement this checking at startup.

アプリ起動時に消耗品をチェックすること.
まず始めにユーザが購入済みの商品をアプリ内課金サービスへ問い合わせる(queryInventoryAsyncを経由して). そして, Inventoryから消費できるPurchaseオブジェクトを取得する.
もし消耗品をユーザが所有している場合, Google Playに消費リクエストを送り, ユーザに提供すること.
これらの実装はTriviaDriveサンプルの例が参考になる.


Testing Your In-app Billing Application

To ensure that In-app Billing is functioning correctly in your application, you should test the test the application before you publish it on Google Play. Early testing also helps to ensure that the user flow for purchasing in-app items is not confusing or slow, and that users can see their newly purchased items in a timely way.

Google Playで公開する前にアプリ内課金が正しく動作しているかテストすること.
事前のテストで商品の購入フローが紛らわしいものでないか, 手間でないかを確認する.
そして, ユーザが新たに購入した商品をタイムリーに確認できることもテストする.

Test with Static Responses

Test your In-app Billing application with static responses by using Google Play’s reserved product IDs. By using reserved product IDs instead of actual product IDs, you can test the purchase flow without specifying an actual payment method or transferring money. To learn more about the reserved product IDs, see Testing In-app Billing.

Google Playで予約された商品IDを使った静的な応答でアプリの課金処理をテストできる.
予約された商品IDは本来の商品IDの代わりとなる.
これにより, 実際にお金を払うことなく購入テストが可能となる.
予約された商品IDについての詳細はTesting In-app Billingを参照.

Test with Your Own Product IDs

Because Google Play does not allow you to use your developer account to directly purchase in-app products that you have created yourself, you’ll need to create test acccounts under your developer account profile. To create a test account, simply enter a valid Google email address. Users with these test accounts will then be able to make in-app-billing purchases from uploaded, unpublished applications that you manage.

Google Playは開発用アカウントで直接商品を購入することを許可していないので, 購入テスト用のアカウントを作成する必要がある.
テストアカウントを作成するために有効なGmailアドレスを登録する.
テストアカウントを持つと, アップロードされた非公開状態のアプリでアプリ内課金をテストできるようになる.

To test your In-app Billing Version 3 application using your own product IDs:

自前の商品IDを使用してIn-app Billing Ver.3を使用した課金アプリをテストするには.

  1. In the Developer Console, add one or more tester accounts to the developer account that you are using to publish your application.
    a. Login to the Developer Console with your developer account.
    b. Click Settings > Account details, then in the License Testing section, add the Google email addresses for your tester accounts.
  2. Build a signed APK file for your In-app Billing application. To learn how to build and sign your APK, see Building Your Application for Release. Make sure that you are using your final (not debug) certificate and private key to sign your application.
  3. Make sure that you have uploaded the signed APK for your application to the Developer Console, and associated one or more in-app products with your application. You don’t need to publish the application on Google Play to test it.
    Warning: It may take up to 2-3 hours after uploading the APK for Google Play to recognize your updated APK version. If you try to test your application before your uploaded APK is recognized by Google Play, your application will receive a ‘purchase cancelled’ response with an error message “This version of the application is not enabled for In-app Billing.”
  4. Install the APK file to your physical test device by using the adb tool. To learn how to install the application, see Running on a Device. Make sure that:
    • Your test device is running on Android SDK Version 2.2 (API level 8) or higher, and is installed with Google Play client Version 3.9.16 or higher.
    • The android:versionCode and android:versionName attributes values in the AndroidManifest.xml of the application that you are installing matches the values of your APK in the Developer Console.
    • Your application is signed with the same certificate that you used for the APK that you uploaded to the Developer Console, before installing it on your device.
  5. Login to the test device by using a tester account. Test your In-app Billing application by purchasing a few items, and fix any issues that you encounter. To learn more about how you can perform large-scale testing of your In-app Billing app, see Test Purchases (In-app Billing Sandbox).
  1. Developer Consoleで1つ以上の購入テスト用アカウントを開発用アカウントに追加し, アプリを共有する
    a. Developer Consoleに開発用アカウントでログイン
    b. Settings > Accountの詳細をクリック, License Testingセクションに購入テスト用のGmailアドレスを追加
  2. 署名された課金アプリのAPKを作成. APKの署名方法についてはBuilding Your Application for Releaseを参照.
  3. Developer Consoleに署名されたアプリをアップロードする. そして, 1つ以上の商品をアプリに関連付ける.
    テストの為にGoogle Playにアプリを公開する必要はない.
  4. adbツールでテスト端末にAPKをインストールする. APKのインストールについてはRunning on a Deviceを参照. 次のことを確認すること:
    • デバイスのSDK Ver.が2.2(API Level8)以上であり, Google Play Ver.3.9.16以上であること
    • インストール前後のAPK署名がDeveloper ConsoleにアップロードされたAPKのものと同じであること
  5. テストデバイス上で購入テスト用アカウントでログインし, 課金アプリでいくつかの商品を購入し,
    テストして見つけた不具合を修正する. In-app Billingアプリの大規模テストについてはTest Purchases (In-app Billing Sandbox)を参照.

License:
Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.

Android:Google Map Utility Library

$
0
0

GMaps Utils

Google Maps Android API Utility Library

discusses the utility library, with a focus on polyline decoding, spherical geometry, and bubble icons:
description
https://www.youtube.com/watch?v=nb2X9IjjZpM

The current version of the library is 0.3.1.

The Utilities

Add heatmaps to your map

heatmap

Heatmaps make it easy for viewers to understand the distribution and relative intensity of data points on a map. Rather than placing a marker at each location, heatmaps use color and shape to represent the distribution of the data. Create a HeatmapTileProvider, passing it a collection of LatLng objects representing points of interest on the map. Then create a new TileOverlay, passing it the heatmap tile provider, and add the tile overlay to the map.

GMap上にデータ分布を視覚化するヒートマップを作成する. ヒートマップはマーカと異なり, データ分布を色や形で表現する. ヒートマップデータ(LatLng)のコレクションをインプットにHeatmapTileProvider作成. HtatmapTileProviderをインプットにTileOverlayを作成してGMapに追加する.

For details, see the documentation on the Google Maps Android Heatmap Utility.

Customise markers via bubble icons

bubble icons

Add a IconGenerator to display snippets of information on your markers. This utility provides a way of making your marker icons look a bit like info windows, in that the marker itself can contain text and other content. The advantage is that you can keep more than one marker open at the same time, whereas only one info window can be open at once. You can also style the markers, change the orientation of the marker and/or content, and change the marker’s background image/nine-patch.

IconGeneratorではGMap上のマーカにスニペットを表示できる. このutilはマーカにちょっとした情報(テキスト等)を付け加えることができる. アドバンテージは複数のマーカに関する情報を同時に開いておくことができる点. マーカスタイル(コンテンツの向き, 背景画像/9patch)を変更することもできる.

Manage marker clusters

clusters

The ClusterManager helps you manage multiple markers at different zoom levels. This means you can put a large number of markers on a map without making the map hard to read. When a user views the map at a high zoom level, the individual markers show on the map. When the user zooms out to a lower zoom level, the markers gather together into clusters, to make viewing the map easier.

ClusterManagerは複数のマーカを異なるズームレベルで管理することをサポートする. 多くのマーカをGMap上に配置すると見通しが悪くなるがそれを回避できる. ハイズームで表示した場合は個別マーカを, ズームアウトすると複数のマーカが寄せ集まったクラスタとして表示する.

For details, see the documentation on the Google Maps Android Marker Clustering Utility.

Encode and decode polylines

polylines

The PolyUtil is useful for converting encoded polylines and polygons to latitude/longitude coordinates, and vice versa.

PolyUtilは多角形(Polylines or Polygons)を指定された経度,緯度を結ぶ形でコンバートする.

polyline
多角形(外線のみ)
polygon
多角形(塗りつぶし)

In Google Maps, the latitude and longitude coordinates that define a polyline or polygon are stored as an encoded string. See the detailed explanation of polyline encoding. You may receive this encoded string in a response from a Google API, such as the Google Directions API.

GMapでは, 定義された多角形をエンコードされた文字列で保存する際に経度と緯度を調整する. See the detailed explanation of polyline encoding. この文字列はGoogle Directions API等から応答される.

You can use PolyUtil in the Google Maps Android API Utility Library to encode a sequence of latitude/longitude coordinates (‘LatLngs’) into an encoded path string, and to decode an encoded path string into a sequence of LatLngs. This will ensure interoperability with the Google Maps API web services.

Google Maps Android API Utility Libraryに含まれるPolyUtilを使って, シーケンシャルな経度/緯度(‘LatLngs’)にエンコードしたり, その反対にデコードすることができる. これはWebサービスとの相互通信にも使用できる.

Calculate distances, areas and headings via spherical geometry

distances

Using the spherical geometry utilities in SphericalUtil, you can compute distances, areas, and headings based on latitudes and longitudes. Here are some of the methods available in the utility:

  • computeDistanceBetween()– Returns the distance, in meters, between two latitude/longitude coordinates.
  • computeHeading()– Returns the bearing, in degrees, between two latitude/longitude coordinates.
  • computeArea()– Returns the area, in square meters, of a closed path on the Earth.
  • interpolate()– Returns the latitude/longitude coordinates of a point that lies a given fraction of the distance between two given points. You can use this to animate a marker between two points, for example.

SphericalUtilには球面幾何学のユーティリティが用意されている. 経度と緯度に基づいて距離, 面積と見出しを計算することができる. 次は代表的なメソッド:

  • computeDistanceBetween()– 経度/緯度をベースにした2地点間の最短パスを計算する. 最短パスは”測地点”と呼ばれ, 球面上では大きな円弧セグメントになる.
  • computeHeading()– 真北を0°とし, 2地点間の角度を時計回りで計算する.
  • computeArea()– ポリゴン領域の面積を平方メートル単位で求める. 閉ループを定義するLatLngオブジェクトを渡す.
  • interpolate()– 球面線形補間を行い, 2地点の間にある座標を返す.

Refer to the reference documentation for a full list of methods in the utility.

Getting started

Android Studio/Gradle

dependencies {
compile 'com.google.maps.android:android-maps-utils:0.3+'
}

Source code

LICENSE

Copyright 2013 Google Inc.

Licensed under the Apache License, Version 2.0 (the “License”);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an “AS IS” BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

以上.


License:
Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 3.0 License, and code samples are licensed under the Apache 2.0 License. For details, see our Site Policies.

https://developers.google.com/maps/documentation/android/utility/

Viewing all 146 articles
Browse latest View live