2012年8月29日水曜日

「AndroidでのGooglePlay課金API活用法」をまとめました

チームEGGの曽川です。

今回はAndroidでのGooglePlay課金API活用法をまとめました。
公式のまとめはこちらにあります。
●Google Play上の課金の方法
・有料販売
 アプリの販売
・In App Billing
 アプリは無料、いわゆるアイテム課金
・SubScription
 月額・年額

●In-App Billing APIs for Google Play
・2011年3月から開始
・背景としては日本・韓国のフィーチャフォンからスマートフォンへの移行
・アプリ内課金の方が儲けられるという見方

●もう一度規約
 原則としてGooglePlayの課金システムを使う
 規約を一言一句読む。
 Googleの人に質問しても断言できない。(審査チームが一定のルールに従っているが、内容は機密事項なため)
 開発者自身で判断をお願いしたい
 
 一度APIを実装すると、どの国であっても課金周りの実装を変える必要はない
 開発者が意識することなく、日本からAT&Tの決済や、逆に海外の人がドコモやauの決済を利用する事が出来る。

●IN_APP_NOTIFY / PURCHASE_STATE_CHNGED が届かない
 多くのケースでアプリ側の対策が不足している事がある
 GoogleがCONFIRM_NOTIFICATIONの計測を行ったところ、まったく問題のないアプリはすぐに返ってくるが、全然返ってこないアプリもある。
 安定しているアプリと安定していないアプリがある
 アプリのActivityが終了している場合(メモリの欠乏)の考慮やブロードキャストの受信失敗が原因ではないか。
 解決策
  1.アプリがどんな状態(メモリ上にない状態)であっても、確実にBroadcastReceiverで受信する
   そしてできるだけ早くCONFIRM_NOTIFICATIONをGoogleに送る。

  2.一旦受信したらまずローカルに保存するなどして決して失わないこと。

●決済が頻繁にキャンセルされることが多いがなぜか?
 キャリア決済の月額上限
  キャリアの年齢での上限設定
  ⇒気付かずにリトライしている
 不審な利用パターン
  クレジットカードの不正使用
  ⇒Googleは評価する処理を行っている
   ⇒テスト中にGoogleが不正と判断する場合がある
    一時的にホールド状態となる
    「配信に時間がかかり過ぎています。」と表示される
 クレジット側の承認が下りなかった
  クレジットカードの上限額など

 クレジットカードのチャージバック
  開発者が対応してくれない場合、クレジットカード会社が代行する
  事業者(開発者)が悪質であるとみなされるので減らすこと
  Google側が即キャンセルを行う場合もある

●課金の履歴についてダブルチェック
 Googleの課金レポート
  月次売レポート
  ⇒最も正確
   経理上で扱うときもこれを使うとよい
   11~13日に更新
  日次販売レポート(3月22日から提供)
  ⇒前日からの速報
 Checkoutからの管理画面からCSV出力
  ⇒件数の制限がある
   件数が少ない場合は利用できる

●Checkout管理画面のTips
 キャンセル操作
  手数料も含めて返金
 決済情報の検索
  見つからない場合は魔法のような「12999763169054705758.」を付与して検索する
 マルチアカウントに対応

●セキュリティ
 時間がないので次回切り出し

●課金機能にかかわるデベロッパーサイト窓口
 英語のみ

●Subscription
 現在はクレジットのみ
 キャリア決済は準備中

●購読開始と終了の流れ
 購読開始
 ⇒REQUEST_PURCHASEメッセージでGooglePlayに
 更新
 ⇒ユーザに更新しましたというメールが送られる
 終了
 ⇒ユーザ
  ⇒GooglePlayのアプリ詳細
   GoogleWallet(PCから)
 ⇒開発者
  ⇒GooglePlayAndroidDeveloperAPIからのみ
   PurchaseTokenを保存する必要

●Subscriptionの有効性の確認
 クライアント側での手段
  ⇒PURCHASE_STATE_CHANGED
   ⇒RESTORE_TRANSACTION
   ⇒負荷がかかって遅くなる場合もある
 開発者側ので手段   ⇒開発者側でサーバを用意する
      GooglePlayAndroidDeveloperAPIを叩く
   必ずデベロッパーコンソールと同じアカウントでAPIをたたくこと

Q.クレジットカードを持っていないユーザへのSubscriptionは?
A.まだできないが、今後できる

Q.GooglePlayギフトカードの展開は?
A.日本での展開は未定

Q.アプリ課金の通知が遅れる場合は?
A.アプリに起因するところが多いのでどんな場合でも通知を受けられるように

Q.Subscription課金のテストは?
A.時間を早める方法は無い
 Subscription機能は余裕を持って開発してほしい

Q.Subscriptionの払い戻しの有効期限を早める方法は?
A.ユーザが登録を停止しても払い戻さない代わりに有効期限が切れるまで有効とするのがGoogle Playのルール。
 途中で切れるのは想定していないので、開発者側のサーバで管理する必要がある

Q.注文テストの場合も料金は発生するか?
A.30%の手数料もGoogleが丸ごと払い戻す
 キャンセルする必要がある
 支払方法の規約は読んでいただきたい

Q.ユーザはSubscriptionを確認できるか
A.確認できる

Q.ステータスページはあるか?GooglePlayがシステムダウン時に補填はあるか?
A.GooglePlayDeveloper英語にすれば表示される
 補填はない

Q.Subscription購入後にorderIdが一致しないが間違いか?
A.orderIdは変わる可能性がある
 purchaseTokenを使う

2012年8月26日日曜日

Google Analytics SDK for Android v2 を訳してみた⑪「EasyTrackerパラメータ」

チームEggの赤峰です。

Google Analytics V2のβ版の翻訳最終回です。
libGoogleAnalyticsV2.jarの使い方が書かれています。
今回はEasyTrackerパラメータについてです。 原文はこちらです。
  Google Analytics V2 - EasyTracker Parameters(原文)

EasyTrackerパラメータ

このSDKは、β版のGoogle Mobile App Analyticsです。
興味ある方はある方は、サインアップしてβ版にアクセスしてください。

このドキュメントは、Android用Google Analytics SDK利用可能なEasyTrackerパラメータの完全なリストのリファレンスを提供します。


(項目)
概要
EasyTrackerパラメータ


[概要]
-------------------------------
アンドロイドv2用のGoogle Analytics SDKでは、あなたのanalytics.xmlファイルで定義されたパラメータを使用してEasyTrackerの実装を構成することができます。以下の表は、バージョン2以降のAndroid用Google Analytics SDKで使用可能なすべてのパラメータです。
パラメータ 型 説明
 ga_trackingId string あなたのデータを送信するためにGoogle AnalyticsのトラッキングIDを指定します。 ID内のダッシュはエンコードしてはいけまんせ。この値を提供しないことによってあなたの追跡を無効にすることができます。
 ga_appName string レポート内のアプリ名のディメンジョンで使用アプリの名前で、パッケージ内で検出された値にデフォルト設定されます。
 ga_appVersion stringレポート内でのアプリのバージョンディメンジョンで使用アプリケーションのバージョンです。パッケージで見つかったバージョンにデフォルト設定されます。
 ga_debug boolean ログにデバッグ情報を書き込んだり有効化するためのフラグで、あなたの実装のトラブルシューティングに役立ちます。デフォルトではfalseです。
 ga_dispatchPeriod integer秒単位のディスパッチ期間です。 30分にデフォルト設定されます。
 ga_sampleFrequency stringサンプルレートで使用します。デフォルトは100.0です。それは、0.0〜100.0の間の任意の値にすることができます
 ga_autoActivityTracking boolean
ユーザーがActivityを起動するたびにスクリーンの自動トラッキングをします。デフォルトではfalse。

 ga_anonymizeIp booleanストレージへのIPアドレスの最後のオクテットを削除することでトラッカーのオブジェクトによって送信された情報を匿名化するためにGoogle Analyticsを指示します。これは少し地理的報告の正確性を軽減することに注意してください。デフォルトではfalsefです。
 ga_reportUncaughtExceptions boolean自動的に例外をキャッチされない例外がアプリケーションでスローされるたびにトラッキングします。デフォルトではfalse。
 ga_sessionTimeout integerアプリケーションがセッションの前に、バックグラウンドでとどまることができる時間(秒単位)の量です デフォルト値は30秒です。負の値はEasyTrackerセッション管理を無効にします。

以上で翻訳完了です。

2012年8月25日土曜日

Google Analytics SDK for Android v2 を訳してみた⑩「ユーザータイミング」

チームEggの赤峰です。

Google Analytics V2のβ版の翻訳第10段です。
libGoogleAnalyticsV2.jarの使い方が書かれています。
今回はユーザータイミングについてです。 原文はこちらです。
  Google Analytics V2 - User Timings(原文)

ユーザータイミング

このSDKは、β版のGoogle Mobile App Analyticsです。
興味ある方はある方は、サインアップしてβ版にアクセスしてください。



(項目)
概要
実装


[概要]
-------------------------------
ユーザのタイミングのトラッキングはGoogle Analyticsの期間をトラッキングするためのネイティブな方法を提供します。例えば、リソースのロード時間をトラッキングすることが有効です。

Google Analyticsのタイミングは以下のフィールドから成ります:
  • String カテゴリ - タイムイベントのカテゴリ測定
  • long インターバル - ミリ秒単位のタイミング
  • String (オプション) 名前 - タイミングイベントの名前
  • String (オプション) ラベル - タイミングイベントのラベル

ユーザータイミングデータは、主にユーザのタイミング·レポートに記載されています。


[実装]
-------------------------------
ユーザータイミングをトラッキングするには、trackTiming()を呼び出しタイミングインターバルと同様にカテゴリを提供します。次の例では、

いくつかのリソース読み込みが終了した後にonLoad()が呼び出されることを想定しています。このケースでは、ゲームのハイスコアのリストです。
public void onLoad(long loadTime) {
  // myTrackerはトラッカーのインスタンスです。
  myTracker.trackTiming(loadTime, "resources", "high_scores", null);
  ... // onLoadコードの残りの部分。
}


次回はEasy Tracker パラメータを翻訳します。

2012年8月24日金曜日

Google Analytics SDK for Android v2 を訳してみた⑨「セッション」

チームEggの赤峰です。

Google Analytics V2のβ版の翻訳第9段です。
libGoogleAnalyticsV2.jarの使い方が書かれています。
今回はセッションについてです。 原文はこちらです。
  Google Analytics V2 - Sessions(原文)

セッション

このSDKは、β版のGoogle Mobile App Analyticsです。
興味ある方はある方は、サインアップしてβ版にアクセスしてください。


このドキュメントでは、Google Mobile App Analyticsのセッションの高レベルの概要を提供し、あなたのアプリケーションでセッションを管理するために使用できるさまざまな方法について説明します。

(項目)

概要
セッションの管理
 自動セッション管理(EasyTracker)
 手動セッション管理


[概要]
-------------------------------
セッションは、あなたのアプリで、ユーザー・インタラクションの単一期間を表わします。ウェブ解析で同等の概念がアクセスです。アクセスのような、セッションは画面ビュー、イベント、およびeコマーストランザクションのように、追跡されたActivityの有用なコンテナとして機能します。

デフォルトでは、Google Mobile App Analyticsでは、同じセッションに互いに30分以内に受信されたヒット曲をグループ化します。しかし、多くの開発者は、アプリがバックグラウンドで動作しているときや、どれくらいだったかのような、アプリの状態をアカウントに取得し、セッション管理の追加レイヤを実装したい場合があります。

このドキュメントの残りの部分では、そのロジックを実装するために使用できるメソッドについて説明します。

あなたのオプションは、完全自動セッション管理から手動で独自のセッション管理ロジックを構築する、またはその両方の組み合わせを使用するまで多様性をもちます。


セッションの管理
-------------------------------
次のセクションでは、あなたのアプリケーションでセッションを管理するために使用可能な方法を説明します。

EasyTrackerを使用した自動セッション管理


EasyTrackerはあなたのための新しいセッションを開始する作業を処理できる自動化されたセッション管理を提供します。自動化されたセッション管理の動作方法の概要は次のとおりです。

・デフォルトの実装では、30秒のセッションタイムアウト期間を持っています。あなたanalytics.xmlファイルにga_sessionTimeoutパラメータを変更することによって、タイムアウト時間を変更することができます。
<-- セッションタイムアウトを60秒で設定 -- >
<integer name="ga_sessionTimeout">60</integer>

・アプリは、セッションタイムアウト期間よりも長いために、バックグラウンドで残っている場合、EasyTrackerは新しいセッションの必要にフラグを設定すると次のヒットは、新しいセッションの一部となります。

・さらに、正確なセッションの長さを取得するには、EasyTrackerはActivityがそのonStop()のコールバックを実行するたびに、イベントトラッキングを使用してイベントをトラッキングします。このイベントは、レポートを表示さrませんが、ユーザーがあなたのアプリを残している場合には、セッションの長さの計算に最後のタイムスタンプとして使用されます。

手動セッション管理


EasyTrackerの自動セッション管理を使用している場合でも、それはあなたのアプリケーションのライフサイクルのキーイベントで手動で新しいセッションを開始すると便利かもしれません。

たとえば、手動で新しいセッションをユーザが正常にあなたのアプリにサインインするたびに起動することもできます。

ユーザのインテントが変更されているか完全に別のユーザであるかもしれないので、サインインに新しいセッションを起動すると、使用状況データを分離して、レポートに理解しやすく保つのを助けます。

新しいセッションを開始するには、setStartSession(true)を呼びます。これは、新しいセッションを開始する必要があることを示す次のトラッキングされたヒットにパラメータを追加します。

以下の例では、onSignIn()は、ユーザが正常にあなたのアプリにサインいつでも呼び出されることを想定しています:
// ユーザが正常にあなたのアプリにサインインした後に呼び出されます。
private void onSignIn() {
  ... // The rest of your onSignIn() code.
  myTracker.setStartSession(true); // myTrackerはトラッカーのインスタンスです。
  myTracker.trackEvent("app_flow", "sign_in", "", null); // 新しいセッションの最初のActivity。
}


次回はユーザータイミングを翻訳します。

2012年8月23日木曜日

Google Analytics SDK for Android v2 を訳してみた⑧「スクリーン」

チームEggの赤峰です。

Google Analytics V2のβ版の翻訳第8段です。
libGoogleAnalyticsV2.jarの使い方が書かれています。
今回はスクリーンについてです。 原文はこちらです。
  Google Analytics V2 - Screen(原文)

スクリーン

このSDKは、β版のGoogle Mobile App Analyticsです。
興味ある方はある方は、サインアップしてβ版にアクセスしてください。



(項目)
概要
自動スクリーントラッキング(EasyTracker)
手動スクリーントラッキング


[概要]
-------------------------------
Google Analyticsのスクリーンは、ユーザーがアプリケーションで表示しているコンテンツを表しています。ウェブ解析のページビューと同様の概念です。画面表示に関するデータを収集することで、どのコンテンツがユーザーによって最も閲覧されて、コンテンツ間遷移を見ることができます。

Google Analyticsのレポートで、画面表示は画面名で使用する単一の文字列フィールドから構成されています。

スクリーンビューのデータは、次のGoogle Analyticsのレポートで主に使用されています。
  • スクリーンレポート
  • エンゲージメント
  • 目標フロー

[実装]
-------------------------------
次のセクションでは、EasyTrackerまたは高度な実装のいずれかを使用して、画面のトラッキングを実装する方法について説明します。あなたがEasyTrackerを使用している場合は、画面の自動トラッキングを実装するオプションがあります。


[自動スクリーントラッキング(EasyTracker)]
-------------------------------
あなたがEasyTrackerを使用している場合は、簡単に画面など、あなたのアプリケーションのアクティビティをスクリーンとしてトラッキングするために自動スクリーンとラッキングを使用することができます。

自動でアクティビティのトラッキングを有効にするには:

1.あなたのアクティビティのすべてにトラッキングメソッドを追加します。
2.あなたのanalytics.xmlファイルのga_autoActivityTrackingパラメータを設定します。
3.あなたのanalytics.xmlファイル内に各アクティビティのスクリーン名を指定します。

これは自動アクティビティトラッキングが有効になっているanalytics.xmlファイルの抜粋例です。
<-- 自動アクティビティとラッキング設定 -->
<bool name="ga_autoActivityTracking">true</bool>

<-- レポートで表示するスクリーン名 -->
<string name="com.example.app.BaseActivity">Home</string>
<string name="com.example.app.PrefsActivity">Preferences</string>

重要:完全なデータを取得するために、アプリのアクティビティすべてにトラッキングメソッドを追加します。

[手動スクリーントラッキング]
-------------------------------
また、手動で呼び出してトラックビュー()で画面表示を追跡することができます。あなたは既にEasyTrackerの自動画面トラッキングを使用している場合でも、あなたのフラグメントまたはアクティビティないかもしれない他のコンテンツを追跡するために、マニュアル画面トラッキングを使用することができます。

手動でtrackView()を呼び出し画面表示をトラッキングすることができます。既にEasyTrackerの自動スクリーントラッキングを使用している場合でも、フラグメントやアクティビティではないコンテンツをトラッキングするために、手動スクリーントラッキングを使用することができます。

trackView()は通常、次の例に示すように、アクティビティまたはフラグメントのOnStart()コールバックで呼び出します。

/**
 * アクティビティやフラグメント内
 */
@Override
public void onStart() {
  super.onStart();
  ... // onStart()の他のコード
  myTracker.trackView("Home Screen"); // myTrackerはトラッカーのインスタンスです。
}


次回はセッションを翻訳します。

2012年8月22日水曜日

Google Analytics SDK for Android v2 を訳してみた⑦「イベント」

チームEggの赤峰です。

Google Analytics V2のβ版の翻訳第7段です。
libGoogleAnalyticsV2.jarの使い方が書かれています。
今回はイベントについてです。 原文はこちらです。
  Google Analytics V2 - Events(原文)

イベント

このSDKは、β版のGoogle Mobile App Analyticsです。
興味ある方はある方は、サインアップしてβ版にアクセスしてください。


(項目)
概要
実装


[概要]
-------------------------------
イベントは、ゲーム内のボタンを押す、または特定のアイテムの使用と同様に、あなたのアプリケーションのインタラクティブなコンポーネントとユーザーの相互作用に関するデータを収集する便利な方法です。

イベントは、あなたのアプリケーションのコンテンツとユーザーのインタラクションを記述するために使用できる4つのフィールドから構成されています:
  • String カテゴリ
  • String アクション
  • String ラベル
  • Long (オプション) 値

SDKは、トークンのスタックを使用してイベントをとランキングするボリュームと周期を制限します。SDKには、最初に60トークンを付与します。Android用Google AnalyticsのSDKでは、トークンの単一のスタックは、すべてのトラッカーで共有されます。トークンは、イベントが追跡されるたびに削除され、トークンは一つの新しいトークンを2秒ごとの割合で再生成されます。


[実装]
-------------------------------
イベントをトラッキングするために、trackEvent()を呼んでください。例えば、ボタンの押下をトラッキングするために、このコードをOnClickListenerのonClickメソッドに加えたりします:
@Override
public void onClick(View v) {
  // myTrackerはトラッカーのインスタンスです。
  myTracker.trackEvent("ui_action", "button_press", "play_button", opt_value);
  ... // 他のクリックでコードを処理します。


次回はスクリーンを翻訳します。

2012年8月21日火曜日

Google Analytics SDK for Android v2 を訳してみた⑥「eコマーストラッキング」

チームEggの赤峰です。

Google Analytics V2のβ版の翻訳第6段です。
libGoogleAnalyticsV2.jarの使い方が書かれています。
今回はeコマースのトラッキングについてです。 原文はこちらです。
  Google Analytics V2 - Ecommerce Tracking(原文)

eコマーストラッキング

このSDKは、β版のGoogle Mobile App Analyticsです。
興味ある方はある方は、サインアップしてβ版にアクセスしてください。


このドキュメントでは、Android用Google AnalyticsのSDKを使用して、アプリ内の支払いと収益を追跡する方法の概要を説明します。

(項目)
概要
実装
 通貨の種類


[概要]
-------------------------------
eコマーストラッキングでは、アプリ内売買をトラッキングすることができます。Google Analyticsのeコマースのデータは、共有されるtransacation IDによって関連した処理とアイテムで一般的に構成されます。
Android用Google AnalyticsのSDKでは、その関係は、トランザクションオブジェクトを作成し、そこにアイテムを追加することによって確立されています。

eコマースのデータは、次のレポートで主に使用されます:
  • eコマースの概要
  • 製品の性能
  • 販売実績
  • 取引
  • 購入する時


[実装]
-------------------------------
トランザクションをトラッキングするための3つのステップがあります:
  1. トランザクション·オブジェクトを作成します。
  2. アイテムオブジェクトを作成し、トランザクションオブジェクトに追加します。
  3. trackTransaction(Transaction transObject)を使用してトランザクションをトラッキングします。

次の例では、ユーザーがアプリ内購入を完了した後onPurchaseCompleted()が呼び出されることを想定しています。
/**
 * 購入処理されました。現在のトランザクションとそれに関連付けられた行のアイテムを追跡しますが、
 * 購入が確認されている場合のみです。The purchase was processed. We will track the transaction 
 */
public void onPurchaseCompleted() {
  Transaction myTrans = new Transaction.Builder(
      "0_123456",                                           // (String) トランザクションId,ユニークにすべき
      (long) 2.16 * 1000000)                                // (long) 注文合計(in micros)
      .setAffiliation("In-App Store")                       // (String) 提携
      .setTotalTaxInMicros((long) 0.17 * 1000000)           // (long) 合計税 (in micros)
      .setShippingCostInMicros(0)                           // (long) 合計配達費(in micros)
      .build();

  myTrans.addItem(new Item.Builder(
      "L_789",                                              // (String) 商品SKU
      "Level Pack: Space",                                  // (String) 商品名
      (long) 1.99 * 1000000,                                // (long) 商品値段(in micros)
      (long) 1 * 1000000)                                   // (long) 商品数
      .setProductCategory("Game expansions")                // (String) 商品カテゴリ
      .build());

    Tracker myTracker = EasyTracker.getTracker(); // トラッカーの参照を取得
    myTracker.trackTransaction(myTrans); // トランザクションをトラッキング
}

通貨の種類


Android用Google AnalyticsのSDKでは、eコマース通貨フィールドはマイクロ(通貨の100万分の1)でなければなりません。

たとえば、4.5991の通貨値を追跡するには、マイクロの値(すなわち、4599100)は、上記の例のように、SDKを使用してトランザクションをトラッキングするときに変換する必要があります。SDKがGoogle Analyticsにトランザクションをディスパッチする場合、その値は自動的に固定小数点式の10進値に変換され、4.5991として送られます。

注:あなたは通貨をマイクロに変換していなければなりませんが、フードの下では、SDKは固定小数点式の10進値を使用してGoogle Analyticsに、通貨データを送信します。これは、その精度は様々な通貨間で失われないようにします。

eコマースの通貨の値は、プロファイルの設定で指定した通貨を使用してレポートに表示されます。通貨記号は、あなたのeコマースのコードに含まれるべきではありません。また、コンマを使用する必要があります。

eコマース通貨フィールドは、払い戻しまたは返品の場合に必要に応じて負の通貨値をサポートしています。


次回はイベントを翻訳します。

2012年8月20日月曜日

Google Analytics SDK for Android v2 を訳してみた⑤「ディスパッチング」

チームEggの赤峰です。

Google Analytics V2のβ版の翻訳第5段です。
libGoogleAnalyticsV2.jarの使い方が書かれています。
今回はディスパッチングについてです。 原文はこちらです。
  Google Analytics V2 - Dispatching(原文)

ディスパッチング

このSDKは、β版のGoogle Mobile App Analyticsです。
興味ある方はある方は、サインアップしてβ版にアクセスしてください。


このドキュメントでは、Android用Google AnalyticsのSDKを使用して、Google Analyticsのバックエンドにクライアントからデータを送信して管理する方法について説明します。

(項目)
概要
定期ディスパッチ
手動ディスパッチ


[概要]
-------------------------------
Android用Google AnalyticsのSDKでは、スクリーンビューやイベントのような収集されたデータはGoogle Analyticsのサーバーに送信される前にキューにローカルに保存されます。Google Analyticsのバックエンドにクライアントから( ここでは"ヒット"として参照する)データを送信されるプロセスは、ディスパッチと呼ばれています。

ディスパッチとは、モバイルコレクションライブラリ固有のものであり、信頼できないネットワークアクセスと限られたバッテリー寿命の課題を軽減するために設計されています。

ディスパッチは2種類あります:
  • 定期ディスパッチ - プログラムまたはあなたのanalytics.xmlファイルのどちらで、定期的な感覚で移動的にヒットをディスパッチする指定します。
  • 手動ディスパッチ - 例えば既存のHTTP接続がある場合、あなたに都合のよい場合にデータを送るため手動でヒットをディスパッチしてください。

ディスパッチの両方のタイプは、SDKのバージョン2でメインUIスレッド外で発生します。

このドキュメントの残りは、ディスパッチの各タイプを掘り下げた内容やあなたのアプリで実装する方法を提供します。

注:データは各プロフィールの地方の時間帯で、翌日に午前4時までにディスパッチされ受け取られるに違いありません。それより遅く受け取られたどんなデータも、報告書に現われません。


[定期ディスパッチ]
-------------------------------
あなたのアプリがGAのデータを集めキューに加えられ、Google Analyticsのバックエンドへ定期的にディスパッチされます。 あなたのアプリケーションがフォアグラウンドまたはバックグラウンドで実行されている場合、定期ディスパッチのいずれか発生する可能性があります。

デフォルトのディスパッチ期間は30分です。あなたのanalytics.xmlファイルにga_dispatchPeriodパラメータを使用して、または、この例のようにsetDispatchPeriod(int dispatchPeriodInSeconds)を呼び出すことによって、秒指定でインターバルを提供することができます。

あなたのanalytics.xmlファイル:
<integer name="ga_dispatchPeriod">60</integer>

プログラム:
GAServiceManager.getInstance().setDispatchPeriod(60);

負の値を設定すると、Google Analyticsに任意のデータを送信する場合は、手動ディスパッチを使用することを必要とし、定期ディスパッチを無効にします。ネットワーク接続が利用可能であれば一方で、0の値を設定すると、すぐにそれぞれのヒットをディスパッチします。

すべてのヒットがディスパッチされたら、定期ディスパッチは、パワー・セーブ·モードに入り、別のトラッキング呼び出しが行われるまで無効になります。

ユーザがネットワークアクセスを失ったり、ディスパッチされるのを待っているヒットが残っている間にアプリケーションを終了した場合、これらのヒットはローカルストレージに保持されます。それらは、次のアプリが動くときやディスパッチが呼ばれたときに送られます。


[手動ディスパッチ]
-------------------------------
定期ディスパッチに依存することとは別に、ヒットを手動でディスパッチしたい時があるかもしれません。
例えば、オーバーヘッドを削減するために、アプリケーションによって行われた他のHTTPリクエストを使用してディスパッチをまとめることができます。

ヒットは、手動でGAServiceManagerインスタンスを使用してディスパッチすることができます。

GAServiceManager.getInstance().dispatch();


次回はeコマースのトラッキングを翻訳します。

2012年8月19日日曜日

Google Analytics SDK for Android v2 を訳してみた④「クラッシュと例外」

チームEggの赤峰です。

Google Analytics V2のβ版の翻訳第4段です。
libGoogleAnalyticsV2.jarの使い方が書かれています。
今回はクラッシュと例外のトラッキングについてです。 原文はこちらです。
  Google Analytics V2 - Crashes and Exceptions(原文)

キャンペーントラッキング

このSDKは、β版のGoogle Mobile App Analyticsです。
興味ある方はある方は、サインアップしてβ版にアクセスしてください。


このドキュメントでは、Android用Google AnalyticsのSDKを使用して、クラッシュや例外データ収集の高レベルな概要を提供します。

(項目)
概要
キャッチされた例外
キャッチされない例外(クラッシュ)
 EasyTrackerの使用
 ExceptionReporterの使用
 ExceptionParserの使用


[概要]
-------------------------------
例外のトラッキングには、あなたのアプリで発生したキャッチまたはキャッチされていないクラッシュや例外の数や種類に関するデータを収集することができます。Google Analyticsでの例外の構成
  • String (省略可な)Description - 例外の説明(最大100文字)。nullを受け付けます。
  • boolean isFatal - 例外が致命的かどうかを示します。trueは致命的を表します。
クラッシュと例外データは、CrashやExceptionレポートで主に使用可能です。


[キャッチされた例外]
-------------------------------
キャッチした例外は、例外処理コードを定義しているあなたのアプリケーションのエラーです。これらは一般的に、あなたのアプリケーションの通常の使用時に発生することが予想され、アプリケーションがデータの要求時にネットワーク接続のタイムアウトのように回復してほしいエラーです。

trackException()をあなたの例外処理コードのcatchブロックに加えることにより、つかまえた例外を追跡することができます。

次の例では、アプリケーションがクラウドからハイスコアのリストをロードしようとします。おそらく要求がタイムアウトすると、低速のネットワーク接続による場合は、ユーザーのためにそれを処理する前にGoogle Analyticsを使用した例外を追跡します:
try {
  ArrayList highScores = getHighScores(); // クラウドからスコアを取得します
  } catch (IOException e) {
  Tracker myTracker = EasyTracker.getTracker(); // トラッカーの参照を取得します
  myTracker.trackException(e.getMessage(), false); // false は致命的でない例外です

  ... // ハイスコアは、現在使用できないことをユーザーに警告が表示されます
}


[キャッチされない例外のトラッキング]
-------------------------------
キャッチされない例外は、アプリケーションが実行時に予期しない条件が発生し、アプリがその結果クラッシュする原因となるしばしば致命的なインスタンスを表します。キャッチされていない例外はEasyTrackerまたはExceptionReporterクラスを使用して自動的に追跡することができます。

EasyTrackerの使用


自動的にEasyTrackerを使用してアプリケーション内のすべてのキャッチされない例外を追跡するために、あなたのanalytics.xmlファイルに次の行を追加します。
<bool name="ga_reportUncaughtExceptions">true</bool>

自動例外トラッキングを使用して例外をトラッキングした後、EasyTrackerはスレッドのデフォルトの例外ハンドラに例外を渡します。

自動例外トラッキングを使用する場合は、次の注意をしてください:
  • 自動例外トラッキングを経由してトラッキングするすべての例外は、Google Analyticsに致命的なものとして報告されます。
  • 自動的にスタックトレースを使用して、説明フィールドに追加されます。

ExceptionReporterの使用


EasyTrackerを使用していない場合、自動キャッチされない例外処理を実装するために、ExceptionReporterクラスを使用してください。これはEasyTrackerがその自動例外レポートを処理するために実装されているものと同じクラスです。

ExceptionReporterはあなたのアプリケーション内の特定のスレッドまたはすべてのスレッドのいずれかの標準のキャッチされない例外ハンドラとして使用できます。例外をトラッキングした後、ExceptionReporterクラスは、例外をあなたが提供するキャッチされない例外ハンドラーに、任意に渡すことができます。

次のコードは、新しいExceptionReporterオブジェクトを作成し、新しいデフォルトのキャッチされない例外ハンドラとして設定します。結果として、すべてのキャッチされない例外がトラッキングされると、前のキャッチされない例外ハンドラに渡されます。ほとんどのアプリケーションでは、デフォルトのハンドラが例外をログに記録し、アプリケーションを終了します。
UncaughtExceptionHandler myHandler = new ExceptionReporter(
    myTracker,                                        // 現在のトラッカーを使用
    GoogleAnalytics.getInstance(),                    // GoogleAnalyticsのシングルトン
    Thread.getDefaultUncaughtExceptionHandler());     // 現在のデフォルトキャッチされない例外ハンドラ

Thread.setDefaultUncaughtExceptionHandler(myHandler); // デフォルトキャッチされない例外ハンドラを作ります。

ExceptionParserの使用


SDKは、キャッチされない例外をトラッキングするときに、スタックトレースから最も関連する説明を取得するために実装するExceptionParserインタフェースを提供します。ExceptionParserインタフェースは一つのメソッド(getDescription(String threadName, Throwable t))を持っています。

この例のようにsetExceptionParser(ExceptionParser parser)を呼び出すことによって、キャッチされない例外をトラッキングするときにExceptionParserを使用するようにトラッカーを設定することができます。
// myParserはExceptionParser実装クラスを表します。
ExceptionParser parser = new myParser(context);

// myTrackerはトラッカーのインスタンスです。
myTracker.setExceptionParser(parser);

あなたのトラッカーが、任意のトラッキングされたキャッチされない例外の説明フィールドに追加するには、パーサのgetDescription()メソッドを使用します。


次回はディスパッチを翻訳します。

2012年8月18日土曜日

Google Analytics SDK for Android v2 を訳してみた③「キャンペーントラッキング」

チームEggの赤峰です。

Google Analytics V2のβ版の翻訳第3段です。
libGoogleAnalyticsV2.jarの使い方が書かれています。
今回はキャンペーントラッキングです。翻訳が難しかったので原文と一緒に見てください。
原文はこちらです。
  Google Analytics V2 - Campaign Tracking(原文)

キャンペーントラッキング

このSDKは、β版のGoogle Mobile App Analyticsです。
興味ある方はある方は、サインアップしてβ版にアクセスしてください。


このドキュメントでは、Android用Google AnalyticsのSDKを使用して、キャンペーンやトラフィックのソースを追跡する方法の概要を説明します。

(項目)
概要
Google Play Store キャンペーントラッキング
 仕組み
 実装
一般的なキャンペーントラッキング
リファラーのトラッキング
既知問題
キャンペーンパラメータ
Google PlayのURL生成

[概要]
-------------------------------
キャンペーントラッキングでは、あなたのアプリケーションのユーザーがどこから来たかを追跡することができます。キャンペーントラッキングは、アプリケーション内のユーザーアクティビティにキャンペーンやトラフィックソースを添えることで、マーケティングチャンネルとリファラの値を向上させ理解することを助けます。

Android用Google AnalyticsのSDKで利用可能ないくつかの種類のキャンペーントラッキングがあります。
  • Google Play Store キャンペーントラッキング - あなたのアプリをダウンロードするためにあなたのアプリのGoogle Play Storeページを参照したキャンペーン、ウェブサイト、アプリを見ます。
  • 一般的なキャンペーントラッキング - アプリがインストールされ起動されたトラフィックソースやキャペーンを見ます。
  • リファラートラッキング - インストールされた後アプリが起動されたWebサイトやアプリケーションなどのトラフィックソースの参照をみます。

次のセッションでは、あなたのアプリにキャンペーントラッキングの各タイプをいつどどのように実装するか説明します


[Google Play Store キャンペーントラッキング]
-------------------------------
Google Play Store キャンペーントラッキングはGoogle Play Storeからあなたのアプリをダウンロードするためにユーザーに送られたキャンペーンやトラフィックソースを確認するすることができます。すべての開発者がGoogle Play Store Trackingを実装することをお勧めします。

Google Play Store キャンペーントラッキングの仕組み


"Google Play Store キャンペーンとラッキング"は、"Google Play Store"からダウンロードされた時点で、あなたのアプリケーションにキャンペーンやトラフィックのソース情報を渡すための"キャンペーン·パラメータ"の使用を信頼します。
以下は、Google Play Storeキャンペーントラッキングの仕組みのエンドツーエンドの説明です。
  1. 広告やウェブサイトやアプリからあなたのアプリのGoogle Play Storeページのリンク対するユーザクリックを取得します。リンクはキャンペーンパラメータでタグ付けされます。
  2. ユーザーがダウンロードしアプリケーションをインストールした後、Google Play Storeは同じキャンペーンのパラメータが含まれているデバイスにINSTALL_REFERRERインテントをブロードキャストします。
  3. あなたのアプリケーションは、キャンペーンのパラメータを読み取り、Google Analyticsのキャンペーン情報を更新するためにそれらを使用して、BroadcastReceiverオブジェクトを使用し、インテントに対応します。

Google Play Store キャンペーントラッキングの実装


Google Play Store キャンペーントラッキングを実装するには:

1.あなたのAndroidManifest.xmlファイルに新しいBroadcastReceiverを追加します。

以下のBroadcastReceiverは、アプリケーションがインストールされた時にGoogle Play StoreによってブロードキャストされるINSTALL_REFERRERインテントに応答することができます。AndroidManifest.xmlファイルに次の項目を追加します。
<!-- インストールリファラトラッキングのために使用される-->
<receiver android:name="com.google.analytics.tracking.android.AnalyticsReceiver" android:exported="true">
  <intent-filter>
    <action android:name="com.android.vending.INSTALL_REFERRER" />
  </intent-filter>
</receiver>

2.Google Play Store リンクにキャンペーンのパラメータを追加する

キャンペーンパラメータはGoogle Analyticsを実装したアプリに、ユーザーをGoogle Play Storeページに紹介したキャンペーンやトラフィックソースに関する情報を渡すために使用されます。

キャンペーンのパラメータ文字列を構築する方法を学ぶために、Google Play URL Builderを使用するか、キャンペーンのパラメータのリファレンスセクションを参照してください。

一度あなたのキャンペーンのパラメータ文字列を構築すると、この例のように、リファラーパラメータの値としては、Google Play StoreのURLに追加します。
https://play.google.com/store/apps/details?id=com.example.app
&referrer=utm_source%3Dgoogle
%26utm_medium%3Dcpc
%26utm_term%3Drunning%252Bshoes
%26utm_content%3DdisplayAd1
%26utm_campaign%3Dshoe%252Bcampaign

Google Play StoreはあなたのアプリケーションのGoogle Analyticsの実装へのリファラーパラメータの値を渡すので、それはGoogleのプレイ·ストアへのリンクに存在していることを確認することが重要です。

AdWordsの自動タグ設定を使用していますか?もしautotaggedされている場合は、Google Play Storeのリンクにタグを追加する手順を行う必要はありません。ウェブトラッキングと同様に、自動タグとモバイルアプリAnalyticsを使用してAdWordsのコンバージョンとしてのアプリケーションの目標のコンプリートをインポートすることができます。AdWordsの自動タグ設定を有効にする方法について説明します。


[一般的なキャンペーントラッキング]
-------------------------------
一般的なキャンペーンのトラッキングは、それらが既にあなたのアプリケーションをインストールした後、ユーザーとのキャンペーンやトラフィックの送信元を関連付けるために使用することができます。

あなたは既にあなたのアプリケーションがインストールされている既存のユーザーに到達するための有償キャンペーンを実行している場合たとえば、どのアプリを起動し、そのキャンペーンの結果であった​​追跡するために、一般的なキャンペーントラッキングを使用することができます。

一般的なキャンペーントラッキングの実装


一般的なキャンペーントラッキングを実装し、setCampaign()を呼び出し、引数としてキャンペーンのパラメータ文字列を渡します。

一般的な実装は、アプリケーションが起動されるときにsetCampaign()を呼び出し、有効なキャンペーンのパラメータが存在があるかどうかを見るために起動インテントを確認します。
public class SampleActivity extends Activity {

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

    // このアクティビティを開始したインテントを取得します
    Intent intent = this.getIntent();
    Uri uri = intent.getData();

    // activityStart()を呼び出す前にキャンペーン情報を更新するEasyTrackerに
    // アクセスできるようにsetContext()を呼び出します。
    EasyTracker.getInstance().setContext(this);

    if (intent.getData() != null) {
      EasyTracker.getTracker().setCampaign(uri.getPath());
    }
    ... // onCreate()コードの省略
  }

  @Override
  public void onStart() {
    super.onStart();
    EasyTracker.getInstance().activityStart(this);
    ... // onStart()コードの省略
  }


  @Override
  public void onStop() {
    super.onStop();
    EasyTracker.getInstance().activityStop(this);
    ... // onStop()コードの省略
  }
}


[リファラートラッキング]
-------------------------------
ユーザーのデバイスであなたのアプリを起動した参照元ソースをトラッキングすることができるで、リファラトラッキングはキャンペーントラッキングと似ていますが、リファラトラッキングはキャンペーン・パラメータの文字列よりもむしろ「google.com」または「myOtherApp」のような単純なストリングを使用します。

"ggogle.com"のようなリファラをトラックする場合、ミディアムディメンションは、暗黙的に"referrer"にセットされている一方、ソースディメンションは"google.com"にセットされます。

参照元のトラッキングや、キャンペーントラッキングと同様に、デフォルトでは、次のトラッキング呼び出しに新しいセッションを開始するようになります。


次の抜粋したコードでは、我々はあなたがGoogle Analyticsのキャンペーンのパラメータ、または照会元を説明する簡単な"referrer"パラメータのどちらかと一緒にアプリが開かれるリンクにタグを持っていることを前提としています。"referrer"パラメータが、他のキャンペーンパラメータがない場合に存在する場合、ユーザーのキャンペーン情報は、新しい参照元で更新されます。

public class SampleActivity extends Activity {

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

    // Activity開始Intentを取得
    Intent intent = this.getIntent();
    uri = intent.getData();

    // activityStart()が呼ばれる前に、キャンペーン情報を更新するEasyTrackerにアクセスできるように、
    // ここでsetContext()を呼びます。
    EasyTracker.getInstance().setContext(this);

    if (uri != null) {
      if(uri.getQueryParmeter("utm_source") != null) {    // 取得できる場合、キャンペーンパラメータを使います。
        EasyTracker.getTracker().setCampaign(uri.getPath()); 
      } else if (uri.getQueryParameter("referrer") != null) {    // それ以外の場合は、リファラーのパラメータを探します。        EasyTracker.getTracker().setReferrer(uri.getQueryParameter("referrer"));
      }
    }
  }


  @Override
    public void onStart() {
    super.onStart();
    EasyTracker.getInstance().activityStart(this);
    ... // onStart()コードの省略
  }


  @Override
  public void onStop() {
    super.onStop();
    EasyTracker.getInstance().activityStop(this);
    ... // onStop()コードの省略
  }
}


[既知問題]
-------------------------------
  • 唯一のBroadcastReceiverクラスはアプリケーションごとに指定することができます。別のSDKから2つ以上のBroadcastReceiversを組み込む必要がある場合は、すべてのブロードキャストを受信し、ブロードキャストの種類ごとに適切な​​BroadcastReceiversを呼ぶ自分のBroadcastReceiverクラスを作成する必要があります。
  • Google Play キャンペーントラッキングは、現在のWebのPlayStoreから開始したWebからデバイスへのインストールをサポートしていません。


[キャンペーンパラメータ]
-------------------------------
キャンペーンパラメータは、あなたのアプリケーションをユーザーにもたらすトラフィックの送信元やキャンペーンに関する情報を渡すために使用されています。
  • 一般的なキャンペーントラッキングでは、エンコードされていないキャンペーンのパラメータ文字列はsetCampaign()に引数として渡されます。
  • Google Play キャンペーントラッキングでは、値としてエンコードされたキャンペーンパラメータ文字の"referrer"パラメータは、あなたのアプリのPlay Storeページの指標となるURLとして追加されます。

以下は、一般的なキャンペーントラッキングを使用することができる有効な、エンコードされていないキャンペーンの文字列の例です。
"utm_campaign=my_campaign&utm_source=google&utm_medium=cpc&utm_term=my_keyword&utm_content=ad_variation1"


以下の表は、Google Playや一般的なキャンペーントラッキングで使用可能なキャンペーンパラメータの全リストです。


パラメータ 説明 例 
 utm_campaign キャンペーン名; 特定の製品プロモーションや戦略的キャンペーンを識別するためのキーワードの分析に使用 utm_campaign=spring_sale
 utm_source キャンペーン元; 検索エンジン、ニュースレター、またはその他のソースを識別するために使用される  utm_source=google
 utm_medium キャンペーンミディアム; 電子メールやクリック単価などの媒体を識別するために使用される(CPC) utm_medium=cpc
 utm_term キャンペーン期間; 広告のキーワードを提供する有料検索で使用する utm_term=running+shoes
 utm_content キャンペーン内容; 同じURLへの広告またはその時点でリンクを区別するためにA / Bテストやコンテンツターゲット広告のために使用 utm_content=logolink 
 utm_content=textlink
 gclid AdWordsの自動タグパラメータ。Googleアドワーズ広告を追跡するために使用される。この値は動的に生成されており、変更されることはありません。


Google Play URL ビルダー
-------------------------------
Google Play キャンペーントラッキング用URLを生成するには、以下のツールを使用します。
パッケージ名:
キャンペーン元:
キャンペーンミディアム:
キャンペーン期間:
キャンペーン内容:
キャンペーン名:
Google Play URL ビルダーを利用される方は本家へ


次回はとクラッシュと例外を翻訳します。

2012年8月17日金曜日

Google Analytics SDK for Android v2 を訳してみた②「高度な設定」

チームEggの赤峰です。

Google Analytics V2のβ版の翻訳第2段です。
libGoogleAnalyticsV2.jarの使い方が書かれています。
今回の②から⑩までは開発ガイド編を訳していきます。
原文はこちらです。
  Google Analytics V2 - Advanced Configuration(原文)

高度な設定

このSDKは、β版のGoogle Mobile App Analyticsです。
興味ある方はある方は、サインアップしてβ版にアクセスしてください。


このドキュメントでは、Android用Google AnalyticsのSDKの高度な設定機能のいくつかの概要を提供しています。

(項目)
概要
複数トラッカーの利用
 トラッカーの管理
 デフォルトトラッカー
アプリレベルのオプトアウト
テストとデバッグ

[概要]
-------------------------------
Android用Google Analytics SDKにはアプリでは、アプリ内のGoogle Analyticsのグローバル状態を管理し、トラッキングの呼び出しを行うために以下のクラスを使用しています。
  • GoogleAnalytics - 新しいトラッカーオブジェクトの構築やアプリレベルのオプトアウト管理を含め、トラッキング実装のグローバルな状態を扱うシングルトンです。
  • GAServiceManager - ディスパッチを含むAndroid用Google Analytics SDKのサーバーサイドを管理するシングルトンです。
  • Tracker - トラッキング呼び出しするクラスで生成されます。複数のTrackerは、独自のトラッキングIDごとにインスタンス化できます。EasyTrackerは、そのトラッキング呼び出しを行うためにTrackerのインタフェースを使用しています。

[複数のトラッカー]
-------------------------------
SDKのバージョン2では、一回の実装でユニークなトラッキングIDごとの複数のトラッカーを使用することができます。すべてのトラッカーは、定期的なディスパッチ間隔、アプリケーションレベルのオプトアウト、デバッグ・モードなどの設定を含めGoogleAnalyticsとGAServiceManagerのシングルトンで保持されているものと同じグローバル状態を共有しています。

EasyTrackerが提供する自動トラッキング機能は、唯一のそれらトラッキング呼び出しを行うために、デフォルトのトラッカーを使用することを念頭に置いてください。これらのEasyTracker機能を使用して、他のトラッカーに似たデータを送信したい場合は、手動で行う必要があります。

トラッカー管理


実装時に複数のトラッカーを管理しやすくするため、必要に応じてトラッカーの生成や取得を行ういくつかのメソッドを提供します。

GoogleAnalyticsのシングルトン取得方法
Context mCtx = this; // 現在のContext取得
GoogleAnalytics myInstance = GoogleAnalytics.getInstance(mCtx.getApplicationContext());

新しいトラッカーの生成方法
Tracker myNewTracker = myInstance.getTracker("UA-XXXX-2") // 新しいトラッキングID

トラッキングIDによる取得方法
Tracker myExistingTracker = myInstance.getTracker("UA-XXXX-Y") // プレスホルダートラッキングID

EasyTrackerで使用されるトラッカーの取得には、まずEasyTrackerにContextを渡します。
// 現在のコンテキスト取得
Context mCtx = this;

// 呼び出し前にEasyTrackerはcontexが必要です
EasyTracker.getInstance().setContext(mCtx);

EasyTrackerのContextが設定されていたら、トラッカーを取得するためにシングルトンを使用することができます。
// トラッカーを取得します。
Tracker myExistingTracker = EasyTracker.getInstance().getTracker();

デフォルトトラッカー


Android用Google AnalyticsのSDKでは、デフォルトのトラッカーの概念があります。1つの実装にもかかわらず複数のトラッカーを持つことができますが、全体で1つのデフォルトのトラッカーを持っています。これをオーバーライドすることができますが最初にインスタンス化したトラッカーが、デフォルトのトラッカーになります。

デフォルトのトラッカー取得方法
Context mCtx = this; // 現在のコンテキストを取得
GoogleAnalytics myInstance = GoogleAnalytics.getInstance(mCtx.getApplicationContext());
Tracker myDefault = myInstance.getDefaultTracker();

デフォルトのトラッカー設定方法
Traker newTracker = myInstance.getTracker("UA-XXXX-2");  // プレスホルダートラッキングID
myInstance.setDefaultTracker(newTracker); // 全体のデフォルトトラッカーとしてnewTrackerを設定

[アプリレベルのオプトアウト]
-------------------------------
あなたは、アプリケーション全体にわたってGoogle Analyticsトラッキングを無効にするための、アプリレベルのオプトアウトのフラグを有効にすることができます。一度設定すると、そのフラグはリセットされるまで持続されます。

アプリレベルのオプトアウトの設定方法
Context mCtx = this; // 現在のcontext取得
GoogleAnalytics myInstance = GoogleAnalytics.getInstance(mCtx.getApplicationContext());
myInstance.requestAppOptOut(new AptOptOutCallback() { // AppOptOutCallbackオブジェクトを受け取ります。
   @Override
   public void reportAppOptOut(boolean optOut) {
     if (optOut) {
     ... // オプトアウトしたことをユーザーに警告します。
     }
   }
}

アプリレベルのオプトアウト設定方法
myInstance.setAppOptOut(appPreferences.userOptOut);


[テストとデバッグ]
-------------------------------
Android用Google AnalyticsのSDKには、ログにトラックの開始やディスパッチされたデータに関する有用な情報を出力するデバッグモードが用意されています。

EasyTrackerを使用してデバッグモードを有効にするには、analytics.xmlリソースファイルに以下を追加します。
<bool name="ga_debug">true</bool>

プログラムでデバッグモードを有効化する方法
Context mCtx = this; // 現在のコンテキスト取得
GoogleAnalytics myInstance = GoogleAnalytics.getInstance(mCtx.getApplicationContext());
myInstance.setDebug(true);

出力はGAV2タグでlogcatに出力されます。

次回はキャンペーントラッキングを翻訳します。

2012年8月16日木曜日

Google Analytics SDK for Android v2 を訳してみた① 「概要」

チームEggの赤峰です。

Google Analytics V2のβ版を利用することになったので、
以下のサイトを翻訳してみました。
つたない翻訳の部分はご了承ください。
     Google Analytics V2(原文)
Google Analytics V2はアプリのアクセス解析に特化されたものとなりました。
ライブラリも従来のlibGoogleAnalytics.jarからlibGoogleAnalyticsV2.jarに変更になります。
移行検討中の方は、ぜひV2へ移行を進めてください。

Google Analytics SDK for Android v2 - 概要

このSDKは、β版のGoogle Mobile App Analyticsです。
興味ある方は、サインアップしてβ版にアクセスしてください。


Android用Google Analytics SDKは、アプリのユーザー関連情報の収集を簡単にします。
このドキュメントは、SDKの値の概要のほか、シングルトラッキングIDとEasyTrackerを使用して、アプリからトラッキングが開始された利用情報を取得するためのガイドを提供します。

v1.xからの移行は? v2を利用を始めるために、移行ガイドをチェックアウトしてください。

(項目)
イントロダクション
始める前に
はじめる
 1. AndroidManifest.xmlの更新
 2. トラッキングメソッドの追加
 3. analytics.xmlの生成
次のステップ

[イントロダクション]
-------------------------------
Android用Google Analytics SDKは、アプリのユーザー関連情報の収集を簡単にします。開発者は、次のようなGoogle Analyticsのアプリケーショントラッキングレポートを使用することができます。
  • アプリを利用しているアクティブユーザー数
  • 世界中のどこでアプリが利用されているか
  • 特定機能の利用と採用
  • アプリ内課金とトランザクション
  • アプリケーションのクラッシュの数と種類
  • その他の有効な測定基準

そのうえ, Google Analytics SDK for Androidは、マーケティングチャネル(GooglePlayからのインストールやアプリ内課金やトランザクションまで、)のパフォーマンスをエンドツーエンドの可視性を提供することにより、モバイルマーケティングキャンペーンの成功を監視するツールを提供します。

[始める前に]
-------------------------------
SDKの実装を開始する前に、次の条件を満たしていることを確認してください。
  • Android developer SDK(Windows版、Mac OS X、およびLinuxで利用可能)
  • Google Analytics SDK for Android v2(libGoogleAnalyticsV2.jarがプロジェクトの/libsディレクトリに含まれ、パスを構築しています。)
  • AndroidV2用のGoogle Analytics SDKを実装し使う事ができる
  • 新しいGoogle Analyticsのアプリケーションプロパティとプロファイル

どこで新しいSDKをダウンロードすればいいですか?
初期β期間中は、SDKのバージョン2はアクティブなβテスターのみが利用できます。βテスターの1人になりたい場合は、Google Analyticsアカウントに新しいプロパティを作成するフローの最後にSDKのバージョン2へのリンクがあります。


[始める]
-------------------------------
SDKの使用を開始するには、次の3つのステップがあります。
  1. AndroidManifest.xmlの更新
  2. トラッキングメソッドの追加
  3. analytics.xmlファイルの生成
これらの手順を完了した後、アプリケーションは、Google Analyticsを使用して次のデータを追跡します。

  • アプリのインストール
  • アクティブユーザーと人口統計
  • 画面とユーザーの関連
  • クラッシュと例外 

[1.AndroidManifest.xmlマニフェストの更新]
------------------------------------------
以下のアクセス許可を追加して、AndroidManifest.xmlファイルを更新します。

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

[2.トラッキングメソッドの追加]
------------------------------------------
それぞれのActivityのonStart()とonStop()メソッドに次の例のようなメソッドを追加してください。
/**
 * Analyticsを実装したActivity例
 */
public class myTrackedActivity extends Activity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  }

  @Override
  public void onStart() {
    super.onStart();
    ... // onStart()のコードの省略
    EasyTracker.getInstance().activityStart(this); // このメソッドを追加
  }

  @Override
  public void onStop() {
    super.onStop();
    ... // onStop()のコードの省略
    EasyTracker.getInstance().activityStop(this); // このメソッドを追加
  }
}

メソッドを呼び出す前にEasyTrackerにContextが必要なことに注意してください。上記の例の中の以下のコードです。

EasyTracker.getInstance.activityStart(this);

contextの設定を管理します。しかし、他のクラスやメソッドでEasyTracker呼び出しを行う必要がある場合は、まずEasyTrackerのsetContext(Context ctx)メソッドを呼び出す必要があります:
Context mCtx = this;  // 現在のコンテキスト取得
EasyTracker.getInstance().setContext(mCtx);  // setContextはアプリケーションコンテキストを取得するためにmCtxを使用します
// EasyTrackerを使用する準備が整いました

[3.analytics.xmlファイルの生成]
------------------------------------------
Android用Google AnalyticsのSDKのバージョン2では、トラッキングの設定と設定オプションがanalytics.xmlのXMLリソースファイルで管理されています。

<?xml version="1.0" encoding="utf-8" ?>

<resources>
  <!--あなたのトラッキングIDで置き換えてください-->
  <string name="ga_trackingId">UA-XXXX-Y</string>

  <!--Activityのトラッキング有効設定-->
  <bool name="ga_autoActivityTracking">true</bool>

  <!--自動例外トラッキングの有効設定-->
  <bool name="ga_reportUncaughtExceptions">true</bool>
</resources>

リントチェッカーがトラッキングIDの('-')記号について警告を表示することがあります。<resources>タグに追加属性を追加することによって、その警告を抑制することができます。

<resources xmlns:tools="https://schemas.android.com/tools"
tools:ignore="TypographyDashes">

重要:ga_trackingId文字列の'-'をエンコードしないでください。
それをしてしまうと、レポート内のデータが見れなくなります。


実装で設定可能なパラメータの完全なリストは analytics.xmlパラメータリファレンスを参照してください。

おめでとう!あなたのアプリケーションは、Google Analyticsを使用してデータを追跡するように設定されました。


[次のステップ]
-------------------------------
アプリ内課金とトランザクション、およびユーザーインタフェースイベント、キャンペーンの追跡など、Google Analyticsのアプリケーション追跡で多くの事を行う事ができます。機能を追加する方法については、次の開発者ガイドを参照してください。

  • 高度な設定 - 複数のトラッカーを使用することを含む高度な構成オプション、詳細について説明します。
  • キャンペーントラッキング - チャネルとのキャンペーンは、アプリのインストールに働いているか理解するためにキャンペーントラッキングを実装する方法を学びます。
  • イベントトラッキング - イベントを使用して、ボタン、ビデオ、およびその他のメディアのようなインタラクティブなコンテンツを使用してユーザーのイベントを追跡する方法について説明します。
  • ユーザータイミング - ロード時間やメディア関連やその他について計測するために、アプリのユーザータイミングを追跡する方法について説明します。
  • Analytics.xmlパラメータ - analytics.xml構成パラメータの完全なリストを参照してください。


次回は高度な設定についてアップします。

2012年8月15日水曜日

「Google Play での Android アプリ提供ことはじめ」をまとめました

チームEGGの曽川です。

「Google Play での Android アプリ提供ことはじめ」が非常に興味深かったのでまとめました。
表現に私の主観も入ってるかもしれませんが、ご了承ください。


1.1 超重要規約はこの2つです
 ・規約を守ること
  DDAは基本的な規約
  Google Play Developer Program Policiesはアプリの詳細の規約
 ・見ていない人は今すぐチェック
  ※参考(garlic_devさんの日本語訳)
 ・今後、関係のないキーワードを入れているアプリは削除される可能性が高い

1.2 その他のポリシー
 ・位置情報、ユーザ同士のコミュニケーションには成熟度の指定があるなどが書かれている。
 ・1.1とあわせて確認してください。


2.1 Featured Graphics
 ・GooglePlay上でアプリアイコンの横に表示される大きな画像
 ・この画像がないとおすすめアプリの対象にならない
 ・色の付け方レイアウトなどは以下から   参考(Android Developer Blog)

2.2 キャリア指定
 ・特別な事情がない限りau,docomo,softbank等の指定はしない
 ・日本とだけ指定する
 ・キャリア課金を避ける理由は、Wi-Fi端末等の場合に課金できない人がいるため。
 ・キャリア課金を避けてGoogle Playの課金を使用する
 ・キャリア課金の場合はWeb版のGoogle Playのランキング対象外となる
  モバイルとWeb版でランキング表示に差が出るので注意

2.3 アプリのアカウント間の移動
 ・アカウント間の移行は正式にサポートしている
 ・現在は英語のみ
 ・ランキングやユーザレビューなども引き継がれる


3.1 Backボタン対応
 ・Backキーは時系列で遡るように意図されている
 ・ゲームアプリなどで、Backキーで「終了しますか?」ダイアログが出るものや、さらにBackキーでダイアログが閉じるだけで終了できないものはダメ
 ・画面内に自前Backボタンがあるものや、自前BackボタンとBackキーの挙動が違うものは良くない
 ・Androidで自前Backボタンは不要だと思う
 ・こういった挙動をするアプリはGoogleのおススメ選定から落ちる
 ・参考
  GoogleIO2012
  GDC 2012のGoogleDeveloperDayレポート(4gamer)
 
3.2 SDカード移動対応
 ・必ずautoかpreferExternalを指定する事
 ・外部メモリに移動したアプリはSDカードに移動しても暗号化される(2.2以降)ので不正利用されない
 ・internalonlyはハッカーに対する防御対策として意味がない

3.3 LockScreen時は音声再生を止める
 ・音声を止める
  onFocusChanged(false),android.intent.action.SCREEN_OFF,onPauseで止める
 ・音声を再開する
  onWindowFocusChanged(true),android.intent.action.USER_PRESENT
 ・音声再開させてはいけない
  onResume,android.intent.action.SCREEN_ON




4.1 ユーザがアプリに求めるもの
 ・ランキングはユーザ評価をもとに評価する
 ・動作が速いもの、待ち時間がないもの、わかりやすいUIのものが必然的に上位に来る
 ・WebみたいなUIを持つアプリはランキング上位には来ない傾向がある
  - Webコンテンツそのままの場合
  - スクロールが多いもの
 ・Androidで気を付けるWebのような動作
  - 待ち時間にグルグルを出してもユーザは待ってくれない
   ⇒きびきび動かせる。表示までを0.2秒未満にするなど
  - リスト表示にしなくていいものはリスト表示にしない
   ⇒ViewPager等を使う
  - あまりスクロールしなくていいものにする
・WebみたいなUIの場合、ユーザはWebでよいと思い評価が下がる
 ・UIの楽しさを求めている

4.2 ユーザ評価はGoogle Playの露出において超重要です。
 ・ユーザ評価をベースに集計している。
 ・単純な売り上げやダウンロード数だけは見ていない
 ・ダウンロード数だけを増やしても伸びない
 ・ユーザに長く使ってもらえるアプリにする

・こんなことやってきました

質問
Q.ユーザの評価はGoogle Playのランキングに関係しているのか?
A.大きくしている。愛されるアプリを。
Q.国際化対応についてどの程度突き詰めるか?中途半端なものでもいいのか
A.コンテンツ次第
  ゲームなどの会話中心のコンテンツはしっかり翻訳した方がいい。
  ツール系のアプリの場合は中途半端な翻訳でもやったほうがよい(アイコンやUIで伝える)
Q.国際化するべき言語は?
A.英語、日本語、韓国語は3巨頭
  あとはドイツ、イタリア、スペイン
Q.GooglePlayのYouTube動画の効果は?
A.有料アプリは効果が大きい
  アプリの世界観・動作の判断に使うので必須
Q.どのようなプラットフォームでテストを行うか
A.一般的な案内は難しい
 ただ、全機種検証はおススメしない(ほぼ不可能)
 GoogleはOSのバージョンごとに代表的な端末をいくつかピックアップしている
 その端末でOKならば、そのバージョンはOKとみなしている
 ただし、端末毎に差分が出そうなアプリ(カメラアプリなど)は端末毎に行う
 どこかで見切りリリースをして、書き込まれた苦情に対して速攻で対応する方が良い
 ただし、リリース後にプロジェクトが解散する場合などはしっかりと検証してほしい
 この件は難しいのでまた別の機会を設けたい

2012年8月12日日曜日

アプリ紹介&軽量なViewのSpace

チームEGGの曽川です。

かわいいネコの動画を紹介する「よるにゃん」をリリースしました。
ぜひ確認してみてください。
Get it on Google Play

今回から「よるにゃん」で実施した実装方法のテクニックなどを紹介していきます。
今回はView同士の空白の実装方法です。
これまでは、ViewやFrameLayoutでその方法を使ってきましたが、Spaceを使用すれば更に高速に空白を表現できます。
これはICS(4.0)以降で追加されたAPIで空白(大きさ)のみを表現し、Viewの背景色等の処理は特に何も行いません。
ICSからのAPIですがソースコードを取り込めばICS以前のバージョンでも使用可能です。
ソースコードを取り込む際は、
[android-sdkディレクトリ]/sources/android-16/android/widget/Space.java
を参照してください。
使い方は以下のとおりです。
    <Space
        android:layout_width="match_parent"
        android:layout_height="50dp" />
先日リリースした「よるにゃん」では、「きょうのねこ」、「まえのねこ」、「せってい」を均等に分割するように利用しています。(ピンク色の部分です。)

どのくらい差が出るのか、以下のコードで1つのViewとSpaceをそれぞれ100万回再描画する時間を計測してみました。(HTC EVO3D OS2.3.4)
                long time = System.nanoTime();

                for (int i = 0; i < 1000000; i++) {
                    view.invalidate();
                }

                long processTime = System.nanoTime() - time;

                System.out.println("time:" + (processTime / 1000000));

View :118.3ms
Space:108.5ms
(※50回の平均です)
ここでは簡単なレイアウトなので微小な差ですが、複雑なレイアウトではさらに計算の負担を減らせると思います。
またレイアウト上で目に見えない部分なので、XML内で「これは空白だよ」と明示的に指定する意味としてもよさそうですね。

2012年8月7日火曜日

TimePickerDialogをFragmentで実装する(処理編)

チームEGGの曽川です。

前回はTimePickerDialogをFragmentで実装しました。しかし、GingerBreadまでの動作は正しいことを確認しましたが、Jelly Beanでは以下の点で動作が想定外でした。(見た目には影響ありませんが...)
・縦横切替でonTimeSetが呼び出される
・バックキーを押すとonTimeSetが呼び出される
・完了を押すとonTimeSetが2回呼び出される
また、DialogFragmentはボタンの連打等で2つ以上のダイアログが重なる場合があります。そのため、以下の要件を満たすTimePickerFragmentを作成しました。
・完了時に1回だけonTimeSetが呼び出される
・連打されても1つしかダイアログが出ない
上記を満たしたコードは以下のとおりです。
public abstract class TimePickerFragment extends ExclusiveDialogFragment implements TimePickerDialog.OnTimeSetListener {

    /** キャンセルフラグ */
    private boolean mIsCancel;
    /** 時間設定コールバック通過フラグ */
    private boolean mIsTimeChange;
    /** フラグメント破棄フラグ */
    private boolean mIsDestory;
    /** 排他的にダイアログが消去されたフラグ */
    private boolean mIsExclusiveDialogDismiss;

    /** 時間設定コールバックで一時的に保存する時間 */
    private int mTmpHour;
    /** 時間設定コールバックで一時的に保存する分 */
    private int mTmpMinute;

    /**
     * {@inherited}
     */
    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mIsCancel = false;
        mIsTimeChange = false;
        mIsDestory = false;
        mIsExclusiveDialogDismiss = false;
        mTmpHour = 0;
        mTmpMinute = 0;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Dialog onCreateDialog(final Bundle savedInstanceState) {
        // プリファレンス読み込み
        final Activity activity = getActivity();

        int theme;
        // GingerBread以前
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
            theme = android.R.style.Theme_Panel;
        } else {
            // HoneyComb以降
            theme = android.R.style.Theme_Holo_Panel;
        }
        // 24時間設定にしている場合はそちらに合わせる
        return new TimePickerDialog(activity, theme, this, getHour(), getMinute(), DateFormat.is24HourFormat(activity));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onTimeSet(final TimePicker view, final int hourOfDay, final int minute) {
        // すでに時間を変更している場合は変更しない
        if (mIsTimeChange) {
            return;
        }

        // JBで完了時にコールバックが2回来る、キャンセル時にコールバックが来る、
        // という2つの事象があったため一時的に値を保存するように修正
        mTmpHour = hourOfDay;
        mTmpMinute = minute;
        mIsTimeChange = true;
    }

    /**
     * {@inherited}
     */
    @Override
    public void onCancel(final DialogInterface dialog) {
        super.onCancel(dialog);
        mIsCancel = true;
    }

    /**
     * {@inherited}
     */
    @Override
    public void onDismiss(final DialogInterface dialog) {
        super.onDismiss(dialog);

        // 時間の更新がない場合は変更しない
        if (mIsTimeChange) {
            return;
        }

        // キャンセル時は処理なし
        if (mIsCancel) {
            return;
        }

        // バックグラウンドで破棄された場合は処理なし
        if (mIsDestory) {
            return;
        }

        // 排他的にダイアログが消去された場合は処理なし
        if (mIsExclusiveDialogDismiss) {
            return;
        }

        onUpdateTime(mTmpHour, mTmpMinute);
    }

    /**
     * {@inherited}
     */
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mIsDestory = true;
    }

    /**
     * {@inherited}
     */
    @Override
    protected void onDismissExclusiveDialog() {
        super.onDismissExclusiveDialog();
        mIsExclusiveDialogDismiss = true;
    }

    /**
     * TimePickerで扱う時間を取得します。
     * 
     * @return 時間
     */
    protected abstract int getHour();

    /**
     * TimePickerで扱う分を取得します。
     * 
     * @return 分
     */
    protected abstract int getMinute();

    /**
     * 時間設定を通知します。
     * 
     * @param hour
     *            時間
     * @param minute
     *            分
     */
    protected abstract void onUpdateTime(final int hour, final int minute);

}
長々と書いてはいますが、このコードではダイアログが完了ボタンを押して消えるときに、一度だけonUpdateTimeというメソッドを呼び出します。
/** キャンセルフラグ */
    private boolean mIsCancel;
    /** 時間設定コールバック通過フラグ */
    private boolean mIsTimeChange;
    /** フラグメント破棄フラグ */
    private boolean mIsDestory;
    /** 排他的にダイアログが消去されたフラグ */
    private boolean mIsExclusiveDialogDismiss;
    .....
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mIsCancel = false;
        mIsTimeChange = false;
        mIsDestory = false;
        mIsExclusiveDialogDismiss = false;
        mTmpHour = 0;
        mTmpMinute = 0;
    }
フィールドで、完了ボタン以外で消える条件をフラグとして持っています。 ・バックキーなどでのキャンセル(mIsCancel) ・OSのメモリ管理により消された(mIsDestory) ・ダイアログが複数出ようとして消された(mIsExclusiveDialogDismiss) もう一つは、onTimeSetが2回実行されないようにする実行済フラグです。 (※通常、フラグメント内で永続化したい変数はフィールドに持たせませんが、ここではライフサイクル上で一度しか使わないため、フィールドとして定義しています。) あとは、それぞれのフラグを適切なタイミングで立てています。 バックキーなどでのキャンセル(mIsCancel)
public void onCancel(final DialogInterface dialog) {
        super.onCancel(dialog);
        mIsCancel = true;
    }
OSのメモリ管理により消された
public void onDestroyView() {
        super.onDestroyView();
        mIsDestory = true;
    }
ダイアログが複数出ようとして消された
protected void onDismissExclusiveDialog() {
        super.onDismissExclusiveDialog();
        mIsExclusiveDialogDismiss = true;
    }
onTimeSetが実行された
public void onTimeSet(final TimePicker view, final int hourOfDay, final int minute) {
        .....
        mIsTimeChange = true;
    }
onTimeSet内では2回目が呼び出されないようにガードして、時間の更新をします。
public void onTimeSet(final TimePicker view, final int hourOfDay, final int minute) {
        if (mIsTimeChange) {
            return;
        }

        mTmpHour = hourOfDay;
        mTmpMinute = minute;
        mIsTimeChange = true;
    }
最後にonDismissでフラグでガードしつつonUpdateTimeを呼び出します。
public void onDismiss(final DialogInterface dialog) {
        super.onDismiss(dialog);

        // 時間の更新がない場合は変更しない
        if (mIsTimeChange) {
            return;
        }

        // キャンセル時は処理なし
        if (mIsCancel) {
            return;
        }

        // バックグラウンドで破棄された場合は処理なし
        if (mIsDestory) {
            return;
        }

        // 排他的にダイアログが消去された場合は処理なし
        if (mIsExclusiveDialogDismiss) {
            return;
        }

        onUpdateTime(mTmpHour, mTmpMinute);
    }
あとは、このTimePickerFragmentを継承するクラスを作成してください。

今回はExclusiveDialogFragmentを継承しましたが、複数ダイアログの表示を考慮しない場合はDialogFragmentを継承して構築してください。その場合Exclusive系のフラグやメソッドは削除します。
コードに不備がありましたので、修正しました。(2012/08/10)
・Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1の条件文
・onDismissメソッドでのmIsTimeChangeのチェック(2.3系のキャンセルボタン押下時にガード)

TimePickerDialogをFragmentで実装する(表示編)

チームEGGの曽川です。

ダイアログはDialogFragmentで実装することが推奨されているので、TimePickerDialogをDialogFragmentで実装しました。(こちらの記事を参考に作成しています。)
しかし記事のソースでは不完全でしたので、以下の2点をカスタマイズしています。
・外部から時間を指定可
・OSバージョンによりテーマを設定
public class TimePickerFragment extends DialogFragment implements OnTimeSetListener {

    /** 時間のキー */
    private static final String KEY_HOUR = "hour";
    /** 分のキー */
    private static final String KEY_MINUTE = "minute";

    /**
     * インスタンスを生成します。
     * 
     * @param hour
     *            時間
     * @param minute
     *            分
     * @return {@link TimePickerFragment}
     */
    public static TimePickerFragment getInstance(final int hour, final int minute) {
        final TimePickerFragment fragment = new TimePickerFragment();
        // 時間の設定
        final Bundle bundle = new Bundle();
        bundle.putInt(KEY_HOUR, hour);
        bundle.putInt(KEY_MINUTE, minute);
        fragment.setArguments(bundle);
        return fragment;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Dialog onCreateDialog(final Bundle savedInstanceState) {
        int theme;
        // GingerBread以前
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
            theme = android.R.style.Theme_Panel;
        } else {
            // HoneyComb以降
            theme = android.R.style.Theme_Holo_Panel;
        }
        // 12 or 24時間設定はユーザ設定に合わせる
        final Activity activity = getActivity();
        final Bundle bundle = getArguments();
        return new TimePickerDialog(activity, theme, this, bundle.getInt(KEY_HOUR), bundle.getInt(KEY_MINUTE), DateFormat.is24HourFormat(activity));
    }

    /**
     * {@inherited}
     */
    @Override
    public void onTimeSet(final TimePicker view, final int hourOfDay, final int minute) {
        // 時間の表示
        Toast.makeText(getActivity(), hourOfDay + ":" + minute, Toast.LENGTH_SHORT).show();
    }

}
まず時間の設定ですが、getInstanceで「時間」と「分」を受け取ってBundleに挿入します。
    public static TimePickerFragment getInstance(final int hour, final int minute) {
        final TimePickerFragment fragment = new TimePickerFragment();
        // 時間の設定
        final Bundle bundle = new Bundle();
        bundle.putInt(KEY_HOUR, hour);
        bundle.putInt(KEY_MINUTE, minute);
        fragment.setArguments(bundle);
        return fragment;
    }
ダイアログを作る際に時間、分をBundleから取得します。
また、DateFormat.is24HourFormatで12時間表記、24時間表記のどちらであるか、ユーザ設定を取得します。
new TimePickerDialog(activity, theme, this, bundle.getInt(KEY_HOUR), bundle.getInt(KEY_MINUTE), DateFormat.is24HourFormat(activity));
テーマ設定は、縦横切替の際に表示がおかしくなる現象が見られた(「分」の値が「時間」に設定される)のと、UIを最新のものに合わせるという2つの理由で行っています。
テーマを指定せずに縦横切替をした画面。(Nexus S[Jelly Bean])

API Level10までとそれ以降で分けています。
       // GingerBread以前
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
            theme = android.R.style.Theme_Panel;
        } else {
            // HoneyComb以降
            theme = android.R.style.Theme_Holo_Panel;
        }
TimePickerFragmentの説明は以上です。
あとは、以下の記述でダイアログを表示します。
                final Calendar calendar = Calendar.getInstance();
                TimePickerFragment.getInstance(calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE)).show(getSupportFragmentManager(), "timepicker");
表示されました。

ここまで実装してきましたが、Jelly BeanとGingerBreadでonTimeSet呼び出しについていくつかの差分があります。(ICSやHoneyCombは確認できていません。)
縦横切替でonTimeSetが呼び出される(Jelly Bean)
バックキーを押すとonTimeSetが呼び出される(Jelly Bean)
完了を押すとonTimeSetが2回呼び出される
onTimeSetで重い処理を行なっていたり、キャンセルで呼び出したく場合などは少し考慮が必要です。
次回はこの問題について解決していきます。

2012年8月4日土曜日

ダイアログが重ならないように制御する

チームEGGの曽川です。

DialogFragmentを表示する場合、複数のDialogFragmentが重なって表示される場合があります。ボタンを連打してダイアログを表示した場合などです。
情報を表示するだけのダイアログであれば良いのですが、何かを選択したり、一度しか行いたくない処理の場合は、複数表示されることが不都合なことがあります。
GoogleMapsではこのような場合に、連打されたら一度消去して再度表示するような動作をしていたので、その方法を実装しました。
public class ExclusiveDialogFragment extends DialogFragment {

    /** ダイアログの共通タグ */
    private static final String TAG = "exclusive_dialog";

    /**
     * タグ指定不可のため、直接の呼び出しは非推奨です。
* {@link #show(FragmentManager)}を利用してください。
     */
    @Deprecated
    @Override
    public final void show(final FragmentManager manager, final String tag) {
        show(manager);
    }

    /**
     * ダイアログを表示します。
     * 
     * @param manager
     *            {@link FragmentManager}
     */
    public void show(final FragmentManager manager) {
        deleteDialogFragment(manager);
        super.show(manager, TAG);
    }

    /**
     * すでに表示されたダイアログを消去します
     * 
     * @param fragmentManager
     *            {@link FragmentManager}
     */
    private static void deleteDialogFragment(final FragmentManager fragmentManager) {

        final ExclusiveDialogFragment prev = (ExclusiveDialogFragment) fragmentManager.findFragmentByTag(TAG);
        // フラグメントが表示されていなければ処理なし
        if (prev == null) {
            return;
        }

        final Dialog dialog = prev.getDialog();
        // ダイアログがなければ処理なし
        if (dialog == null) {
            return;
        }

        // ダイアログが表示されていなければ処理なし
        if (!dialog.isShowing()) {
            return;
        }

        // ダイアログ消去通知と消去
        prev.onDismissExclusiveDialog();
        prev.dismiss();
    }

    /**
     * ダイアログが排他的消去される際に呼び出されます。
     */
    protected void onDismissExclusiveDialog() {
        // 継承先で処理を実装
    }

}
まず、DialogFragmentを表示するタグを固定します。
/** ダイアログの共通タグ */
    private static final String TAG = "exclusive_dialog";
すでにダイアログが表示されていれば、そのタグを使ってダイアログを削除します。
private void deleteDialogFragment(final FragmentManager fragmentManager) {
    final ExclusiveDialogFragment prev = (ExclusiveDialogFragment) fragmentManager.findFragmentByTag(TAG);
      ...
    prev.dismiss();
}
このクラスを継承してDialogFragmentを使用しますが、タグを固定しているので通常の呼び出し方法ではなく以下のメソッドを使います。(※show(FragmentManager manager,String tag)もタグは無視されますが使用できるようにはしています。)
public void show(final FragmentManager manager){}
これで、ダイアログが複数表示されなくなります。
また事前にフラグメントに処理を行いたい際には、onDismissExclusiveDialogをオーバライドして処理を記述します。

2012年8月2日木曜日

WebViewのエラー画面をカスタマイズする

チームEggの赤峰です。

さて、本日はWebViewの話です。
動的更新が発生するページなどは、WebViewを利用することで
外部に用意したWebページとして表示させることができます。

しかし、WebViewをアプリの一部として自然に表示させるにはカスタマイズが必要です
今回は、以下の2点について調整していきます。
①ページ読み込み時の表示
②ページ取得不可時のエラーページ表示
読み込み中の画面は白画面なので省略します。

ネイティブアプリに近付けるために以下の変更を行います。
①ページ読み込み時の表示
⇒ 読み込みスピナ表示
②ページ取得不可時のエラーページ表示
⇒ 通信エラー画面用View表示
では、レイアウトXMLとコードを作成します。
まずは、WebView、スピナ、エラーページViewを含むレイアウトです。
activity_webview_with_spinner.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white" >
    <WebView
        android:id="@+id/webview_main"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:fadeScrollbars="false"
        android:fadingEdge="vertical"
        android:scrollbarAlwaysDrawVerticalTrack="true" />
    <TextView
        android:id="@+id/webview_error_page"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white"
        android:text="ページの取得に失敗しました"
        android:textColor="@android:color/black"
        android:textSize="20dp"
        android:visibility="gone" />
    <ProgressBar
        android:id="@+id/loading_spinner"
        style="@android:style/Widget.ProgressBar.Inverse"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:indeterminate="true"
        android:visibility="gone" />
</FrameLayout>

次に、WebView表示用のアクティビティです。
WebViewActivity.java
public class WebViewActivity extends Activity {

    /** WebView */
    private WebView mWebView;
    /** スピナ */
    private View mLoadingSpinner;
    /** エラーページ */
    private View mErrorPage;

    /** ページ取得失敗判定 */
    private boolean mIsFailure = false;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview_with_spinner);
        mIsFailure = false;
        mLoadingSpinner = findViewById(R.id.loading_spinner);
        mWebView = (WebView) findViewById(R.id.webview_main);
        mWebView.setWebViewClient(this.mWebViewClient);
        mErrorPage= findViewById(R.id.webview_error_page);
        mWebView.post(new Runnable() {
            @Override
            public void run() {
                mWebView.getSettings().setJavaScriptEnabled(true);
                mWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(false);
                mWebView.loadUrl("http://www.google.com");
            }
        });
    }

    /** WebViewClientクラス */
    private WebViewClient mWebViewClient = new WebViewClient() {
        /**
         * @see android.webkit.WebViewClient#onPageStarted(android.webkit.WebView, java.lang.String, android.graphics.Bitmap)
         */
        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
            mLoadingSpinner.setVisibility(View.VISIBLE);
            mWebView.setVisibility(View.GONE);
            mErrorPage.setVisibility(View.GONE);
        }

        /**
         * @see android.webkit.WebViewClient#onPageFinished(android.webkit.WebView, java.lang.String)
         */
        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            if (mIsFailure) {
                mErrorPage.setVisibility(View.VISIBLE);
            } else {
                mErrorPage.setVisibility(View.GONE);
            }
            mWebView.setVisibility(View.VISIBLE);
            mLoadingSpinner.setVisibility(View.GONE);
        }

        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            mIsFailure = true;
        }
    };

    /**
     * {@inheritDoc}
     */
    @Override
    public void onBackPressed() {
        // Back出来る場合、WebViewのBackを実行
        if (mWebView != null && mWebView.canGoBack()) {
            mWebView.goBack();
        } else {
            super.onBackPressed();
        }

    }

}

レイアウトXMLに定義した、WebView、スピナ、エラー画面Viewをinflateします。
各Viewの表示状態を変更するためにWebViewClientで3つのメソッドをオーバーライドします。
 ①onPageStarted…ページ読み込み時に実行
 ②onRecivedError…ページ取得エラー時に実行。
  ※エラーが発生した場合のみ②の後に③が呼ばれます。
 ③onPageFinished…読み込み完了時に実行

各処理内容を見ていきます。
①onPageStarted
スピナを表示状態にし、WebViewとエラー画面Viewは非表示にします。
public void onPageStarted(WebView view, String url, Bitmap favicon) {
    super.onPageStarted(view, url, favicon);
    mLoadingSpinner.setVisibility(View.VISIBLE);
    mWebView.setVisibility(View.GONE);
    mErrorPage.setVisibility(View.GONE);
}
②onRecivedError
エラー用のフラグをtrueにします。
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
    mIsFailure = true;
}
③onPageFinished
スピナを非表示にし、WebViewを表示状態にします。
エラーページ画面は、②のフラグの状態で可視状態を設定しています。
public void onPageFinished(WebView view, String url) {
    super.onPageFinished(view, url);
    if (mIsFailure) {
        mErrorPage.setVisibility(View.VISIBLE);
    } else {
        mErrorPage.setVisibility(View.GONE);
    }
    mWebView.setVisibility(View.VISIBLE);
    mLoadingSpinner.setVisibility(View.GONE);
}

コードは簡略化しているので再読み込みを行う場合等はカスタマイズが必要です。
WebViewのバックキー制御とスピナ―変更については次回記載します。

2012年8月1日水曜日

AdmobをFragmentで実装しました

チームEGGの曽川です。

広告の処理をActivityごとに書くのが面倒だったので、AdMobをフラグメントで実装しました。(GoogleAdMobAdsSdk-6.1.0を使用)
まずはベースとなるAdmobFragmentクラスです。必要最低限の機能だけ実装しています。
/**
 * Admobのフラグメントです。
* ※このフラグメントを使用するとloadAdOnCreate=trueの状態となります。
 * 
 * @author sogawa
 * 
 */
public abstract class AdMobFragment extends Fragment {

    /** {@link AdView} */
    private AdView mAdView;

    /** {@link AdRequest} */
    private AdRequest mAdRequest;

    /**
     * {@inherited}
     */
    @Override
    public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
        mAdView = new AdView(getActivity(), getAdSize(), getUnitId());
        mAdRequest = new AdRequest();
        // リスナの設定
        final AdListener listener = getAdListener();
        if (listener != null) {
            mAdView.setAdListener(listener);
        }
        return mAdView;
    }

    /**
     * {@inherited}
     */
    @Override
    public void onActivityCreated(final Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mAdView.loadAd(mAdRequest);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mAdView.destroy();
    }

    /**
     * 広告のサイズを設定します。
     * 
     * @return {@link AdSize}
     */
    protected abstract AdSize getAdSize();

    /**
     * ユニットIDを指定します。
     * 
     * @return ユニットID
     */
    protected abstract String getUnitId();

    /**
     * {@link AdListener}を取得します。
* デフォルトでは何も行いません。
     * 
     * @return {@link AdListener}
     */
    protected AdListener getAdListener() {
        return null;
    }

}
次にAdMobFragmentを継承したAdMobFragmentImplクラスを作成します。
AdMobFragmentImplクラスではAdSize、UnitId、必要に応じてリスナを設定します。
public class AdMobFragmentImpl extends AdMobFragment {

    /**
     * {@inherited}
     */
    @Override
    protected AdSize getAdSize() {
        return AdSize.BANNER;
    }

    /**
     * {@inherited}
     */
    @Override
    protected String getUnitId() {
        return "XXXXXXXXXXXXXX";
    }
}
あとは、AdMobを入れたい箇所にFragmentの記述をするだけで、利用することができます。
<fragment
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        class="com.hoge.test.AdMobFragmentImpl" />
AdMobFragmentを共通のライブラリとして使い、AdMobFragmentImplをプロジェクトごとに実装すると使いやすいと思います。
※追記
2012/08/15 Admob読み込み中に中断するときに不具合がありましたので、onResumeで広告ダウンロードを停止する処理を削除しました。