2011年6月29日水曜日

Android In App Billing サンプルアプリを試す

Downloading the Sample Application

In-app billing サンプルアプリのソースファイル

  • IMarketBillingService.aidl
    Android Market の in-app billing service (MarketBillingService) との IPC インタフェースを定義する Android Interface Definition Library (AIDL) ファイル

  • Dungeons.java
    購入画面と購入履歴画面の UI を提供するサンプルアプリファイル

  • PurchaseDatabase.java
    購入情報を保存するためのローカスデータベース

  • BillingReceiver.java
    Android Market からの非同期なレスポンスメッセージ (broadcast intents) を受信する BroadcastReceiver。すべてのメッセージを BillingService に転送する

  • BillingService.java
    MarketBillingService に接続して (binding) 、アプリに代わって Android Market にメッセージを送る Service

  • ResponseHandler.java
    購入データベースと UI をアップデートするためのメソッドを含んだ Handler

  • PurchaseObserver.java
    購入に関連した変更を観測するための抽象クラス

  • Security.java
    いくつかのセキュリティに関連したメソッドを提供する

  • Consts.java
    いくつかの Android Market 定数とサンプルアプリの定数を定義する。Android Market によって定義されている全ての定数は自分のアプリケーションで同じように定義されている必要があります。

  • Base64.java, Base64DecoderException.java
    バイナリから Base64 エンコーディングへの変換サービスを提供する。Security.java クラスはこれらのユーティリティクラスに依存している


in-app billing サンプルアプリは Android SDK のコンポーネントとしてダウンロード可能です。
Android SDK and AVD Manager を起動して、 "Google Market Billing package" コンポーネントを選択し、 Install Selected をクリックしてダウンロードを開始します。



ダウンロードが完了したら、Android SDK and AVD Manager はコンポーネントを次のディレクトリに保存します。

<sdk>/extras/google/market_billing/

in-app billing の end-to-end のデモが見たいなら、in-app billing を自分のアプリに組み込む前にサンプルアプリケーションをビルドして実行することができます。サンプルアプリをビルドして実行するには次の3つのタスクを行います。

  • サンプルアプリを設定しビルドする
  • サンプルアプリを Android Market にアップロードする
  • テストアカウントを設定してサンプルアプリを実行する



■ Configuring and building the sample application

サンプルアプリを実行する前に、いくつか設定が必要です。

  • 1. サンプルアプリのコードに自分の Android Market public key を追加する
    これにより、Android Market から返されるトランザクション情報の署名をアプリが検証できるようになります。サンプルアプリのコードに自分の public key を追加するには

    • 1. Android Market publisher account にログインする
    • 2. 左上の名前の下の "プロフィールの編集" をクリックする
    • 3. プロフィールの編集ページで、"ライセンスとアプリ内課金" パネルまでスクロールする
    • 4. 公開鍵をコピーする
    • 5. src/com/example/dungeons/Security.java をエディタで開く
    • 6. 公開鍵を次のコードに追加する
      String base64EncodedPublicKey = "your public key here";
    • 7. ファイルを保存する

  • 2. サンプルアプリのパッケージ名を変更する
    現在のパッケージ名は com.example.dungeons になっていますが、Android Market に com.example が含まれたパッケージ名のアプリをアップロードすることはできないので、他のパッケージ名に変更してください。
  • 3. サンプルアプリをリリースモードでビルドし署名する
    アプリのビルドと署名方法は Building and Running を参照してください。



■ Uploading the sample application

サンプルアプリのリリースバージョンビルドと署名が終わったら、Android Market publisher site にドラフトとしてアップロードする必要があります。また、サンプルアプリ内で購入することができる in-app アイテム用のプロダクトリストを作成する必要もあります。

  • 1. サンプルアプリのリリースバージョンを Android Market にアップロードする
    サンプルアプリケーションを公開しないでください。非公開のドラフトアプリのままにしておきます。サンプルアプリはデモを目的としたもので Android Market で一般公開できるようには作られていません。Android Market にアプリをアップロードする方法は Uploading applications を参照してください。

  • 2. サンプルアプリ用のプロダクトリストを作成する
    サンプルアプリでは2つのアイテムを購入できます。a two-handed sword (sword_001) と a potion (potion_001) です。自分のプロダクトリストを設定することを推奨します。つまり、sword_001 の購入タイプを "Managed per user account" ("ユーザーアカウントごとに管理") にし、potion_001 の購入タイプを "Unmanaged" ("管理なし") にすることで、これら2つの購入タイプの振る舞いを見ることができます。プロダクトリストの設定方法は Creating a Product List を参照してください。


注意: サンプルアプリを公開しない場合でも、プロダクトリストにアイテムを公開しなければなりません (sword_001potion_001)。 さらに、サンプルアプリのプロダクトリストにアイテムを追加するには Google Checkout Merchant account が必要です。


■ Running the sample application

エミュレータではサンプルアプリを実行することができません。サンプルアプリはデバイスで実行する必要があります。

  • 1. Android Market publisher account に少なくとも1つ以上のテストアカウントが登録されていることを確認する
    自分で自分のアイテムを購入することはできません (Google Checkout がこれを禁止しています)、よって、サンプルアプリのアイテムを購入することが可能なテストアカウントを少なくとも1つ以上作成する必要があります。テストアカウントの設定方法は Setting up Test Accounts を参照してください。

  • 2. Android Market application もしくは MyApps application のサポートされているバージョンが走っていることを確認する
    Android 3.0 もしくは 3.1 が走っているデバイスなら、in-app billing は version 5.0.12 (以上の) MyApps application が必要です。それ以外のバージョンの Android が走っているデバイスなら、in-app billing は version 2.3.4 (以上の) Android Market application が必要です。Android Market application のバージョンを確認する方法は Updating Android Market を参照してください。

  • 3. デバイスにアプリをインストールする
    Android Market にアプリケーションがアップロードされていても、公開されていないので、Android Market からデバイスにダウンロードすることはできません。代わりに自分でデバイスにアプリケーションをインストールしなければなりません。デバイスにアプリケーションをインストールする方法は Running on a device を参照してください。

  • 4. テストアカウントの1つをデバイスのプライマリアカウントに設定する
    デバイスのプライマリアカウントは Android Market で登録したテストアカウントの1つである必要があります。デバイスのプライマリアカウントがテストアカウントでない場合、デバイスをファクトリーリセットしてテストアカウントの1つでサインインする必要があります。ファクトリーリセットを行うには

    • 1. デバイスの設定を開く
    • 2. Privacy をタップする
    • 3. Factory data reset をタップする
    • 4. Reset phone をタップする
    • 5. 電話がリセットされたら、デバイスのセットアッププロセスでテストアカウントでサインインする


  • 5. アプリケーションを実行して sword や potion を購入する
    テストアカウントを使ってアイテムを購入した場合、テストアカウントは Google Checkout を通して請求され、自分の Google Checkout Merchant account は購入の支払いを受け取ります。そのため、テストアカウントによる購入を払い戻ししたい場合があるでしょう。そうでなければ実際の購入として自分の merchant account に表示されます。


注意 : サンプルアプリのデバッグログはデフォルトではオフになっています。Consts.java ファイルの DEBUGtrue に設定することでオンにできます。


 

2011年6月27日月曜日

Android 左側にマークを置ける CheckedTextView を作った

フレームワークの CheckedTextView はチェックマークの場所が右側固定なので、左にもできるようにしたカスタムビューを作りました。

CheckedTextView - yanzm-s-Custom-View-Project at github -

使い方はこんな感じ


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/yanzm.products.customview"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<yanzm.products.customview.CustomCheckedTextView
android:text="CustomCheckedTextView"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_vertical"
android:paddingLeft="30dip"
android:paddingRight="6dip"
app:checkMark="?android:attr/textCheckMark"
app:checked="true"
/>

<yanzm.products.customview.CustomCheckedTextView
android:text="CustomCheckedTextView"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_vertical"
android:paddingLeft="30dip"
android:paddingRight="6dip"
app:checkMark="?android:attr/textCheckMark"
app:checked="true"
app:drawablePadding="10dip"
/>


<yanzm.products.customview.CustomCheckedTextView
android:text="CustomCheckedTextView"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_vertical"
android:paddingLeft="40dip"
android:paddingRight="6dip"
app:checkMark="?android:attr/textCheckMark"
app:checked="true"
app:position="right"
/>

<yanzm.products.customview.CustomCheckedTextView
android:text="CustomCheckedTextView"
android:id="@+id/customCheckedTextView1"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_vertical"
android:paddingLeft="40dip"
android:paddingRight="6dip"
app:checkMark="?android:attr/textCheckMark"
app:checked="true"
app:position="right"
app:drawablePadding="30dip"
/>

<CheckedTextView
android:text="CustomCheckedTextView"
android:id="@+id/customCheckedTextView1"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_vertical"
android:checkMark="?android:attr/textCheckMark"
android:paddingLeft="40dip"
android:paddingRight="6dip"
android:checked="true"
/>

</LinearLayout>






設定できるパラメータは

  • checkMark : reference チェックマークのリソースID
  • checked : boolean 初期状態
  • position : "left | right" チェックマークの位置
  • drawablePadding : dimension チェックマークと文字の間の余白


です。
Android Library Project としてインポートして使えます。


 

declare-styleable メモ

公式のリファレンスサイトだと、declare-styleable の記事が
Hello Views の GalleryFragment の説明 と Sample アプリ (API Demo とか) しかなかったので、format とかのメモ。

SYNTAX:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="styleable_name">
<attr name="format_name" format="string|integer|float|boolean|color|dimension|reference">
<enum name="enum_name" value="enum_value" />
<flag name="flag_name" value="flag_value" />
</attr>
</declare-styleable>
</resources>


ELEMENTS:
  <resources>
    必須。ルートノードでなければならない。

    属性なし。

  <declare-styleable>
    styleable を定義する。
    <attr> エレメントを含まなければならない。

    ATTRIBUTES:
      name
        String必須。styleable エレメントの名前。
        View の設定値を取得するための resource ID として
        使用される。

  <attr>
    styleable の1つのプロパティを定義する。
    <declare-styleable> の子エレメントでなければならない。

    ATTRIBUTES:
      name
        String必須。styleable プロパティの名前を定義する。

      format
        String。 styleable プロパティのフォーマット名。
        "string", "integer", "float", "boolean", "color",
        "dimension", "reference" のいずれか。

  <enum>
    styleable プロパティのフォーマットの1つのを定義する。

    ATTRIBUTES:
      name
        String必須。enum プロパティの名前を定義する。

      value
        String必須。整数。enum プロパティの値を定義する。

  <flag>
    styleable プロパティのフォーマットの1つのを定義する。

    ATTRIBUTES:
      name
        String必須。flag プロパティの名前を定義する。

      value
        String必須。16進数、整数。
        flag プロパティの値を定義する。

EXAMPLE:
XML ファイルは res/values/attrs.xml に保存される。


<declare-styleable name="LabelView">
<attr name="text" format="string" />
<attr name="textColor" format="color" />
<attr name="textSize" format="dimension" />
</declare-styleable>



<declare-styleable name="DraggableDot">
<attr name="radius" format="dimension" />
<attr name="legend" format="string" />
<attr name="anr">
<enum name="none" value="0" />
<enum name="thumbnail" value="1" />
<enum name="drop" value="2" />
</attr>
</declare-styleable>



<declare-styleable name="Theme">
<attr name="windowSoftInputMode">
<flag name="stateUnspecified" value="0" />
<flag name="stateUnchanged" value="1" />
<flag name="stateHidden" value="2" />
<flag name="stateAlwaysHidden" value="3" />
<flag name="stateVisible" value="4" />
<flag name="stateAlwaysVisible" value="5" />

<flag name="adjustUnspecified" value="0x00" />
<flag name="adjustResize" value="0x10" />
<flag name="adjustPan" value="0x20" />
</attr>
</declare-styleable>

2011年6月23日木曜日

Android Dialog のタイトル部分をなくす (XMLスタイル編)

コードからダイアログのタイトル部分をなくす方法は以前紹介しました。

Y.A.M の 雑記帳: Android Dialog のタイトル部分をなくす

今回はスタイルを使った方法です。

res/values/styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MyDialogTheme" parent="@android:style/Theme.Dialog">
<item name="@android:windowNoTitle">true</item>
</style>
</resources>



Dialog dialog = new Dialog(this, R.style.MyDialogTheme);


複数の種類のダイアログでタイトル部分をなくす場合など、
スタイルとして共通化させておくとコードがすっきり!


 

2011年6月16日木曜日

Android AppWidget

ベースとなる package は android.app.widget です。

app widget とは、ホームスクリーンのような別のアプリに埋め込むことができるミニチュアアプリのようなもので、新しい Activity を起動せずにアプリケーションのデータやサービスに簡単にすばやくアクセスすることができます。

詳しくは App Widget developer guide を参照すること。

どんなアプリケーションでも app widget provider として app widget を発行できます。app widget を発行するためにアプリケーションが行うことは ACTION_APPWIDGET_UPDATE intent を受け取る BroadcastReceiver と app widget についての metadata を提供することだけです。Android では、BroadcastReceiver を継承した AppWidgetProvider クラスを提供しています。AppWidgetProvider は、app widget の振る舞いを定義し、broadcast の処理を援助する便利なクラスです。

app widget host が widget を配置するためのコンテナです。widget のルック&フィールの詳細のほとんどは host に任されています。例えば、ホームスクリーンはウィジェットを見る方法の一つですが、ロックスクリーンも同様に widget を含むことができます。この場合、追加、削除、その他の widget 管理に関する別の方法を持つことになります。

このパッケージ内のクラスは以下のとおり

AppWidgetHost
  AppWidget サービスとのやり取りを、自身の UI 内に AppWidget を
  埋め込みたいアプリ(ホームスクリーンとか)に提供する

AppWidgetHostView
  AppWidget のビューを表示するためのグルー(糊)を提供する

AppWidgetManager
  AppWidget の状態をアップデートする。インストールされている
  AppWidget provider の情報や他の AppWidget の関連する状態を
  取得する

AppWidgetProvider
  AppWidget provider の実装を支援する便利なクラス

AppWidgetProviderInfo
  インストールされている AppWidget provider のメタデータを
  記述する
  このクラスのフィールドは <appwidget-provider> XML タグに
  対応する

この他に関連するクラスとして ComponentNameRemoteView があります。

ComponentName
  利用可能な特定のアプリケーションコンポーネント(Activity,
  Service, BroadcastReceiver, ContentProvider) の識別子
  コンポーネントを識別するために必要な2つの情報がここに
  カプセル化される。1つは存在しているパッケージ名(String)、
  もう1つはパッケージ内のクラス名(String)

RemoteViews
  他のプロセスに表示可能な View 階層を記述したクラス
  階層はレイアウトリソースファイルからインフレートされ、
  インフレートされた階層のコンテンツを編集する基本的な操作を
  提供する


---

AppWidgetManager のインスタンスを取得するには、getInstance(Context context) を使います。


AppWidgetManager manager = AppWidgetManager.getInstance(this);


現在インストールされている AppWidget の一覧を取得するには getInstalledProviders() を使います。


List<AppWidgetProviderInfo> widgetList = manager.getInstalledProvider();


AppWidgetProviderInfo は Parcelable を implements しているクラスです。
Android 3.1 (API Level 12) から resizable feature 用の定数
 ・RESIZE_BOTH
 ・RESIZE_HORIZONTAL
 ・RESIZE_NONE
 ・RESIZE_VERTICAL
が追加されています。

各フィールドと XML の attribute の関係は次の通り

・int autoAdvanceViewId - android:autoAdvanceViewId
  (AppWidget meta-data file)
  API Level 11 (Android 3.0) から

・ComponentName configure - android:configure
  (AppWidget meta-data file)

・int icon - android:icon
  (AndroidManifest.xml の <receiver>)

・int initialLayout - android:initialLayout
  (AppWidget meta-data file)

・String label - android:label
  (AndroidManifest.xml の <receiver>)

・int minHeight - android:minHeight
  (AppWidget meta-data file)

・int minWidth - android:minWidth
  (AppWidget meta-data file)

・int previewImage - android:previewImage
  (AndroidManifest.xml の <receiver>)
  API Level 11 (Android 3.0) から

・ComponentName provider - android:name
  (AndroidManifest.xml の <receiver>)

・int resizeMode - android:resizeMode
  (AppWidget meta-data file)
  API Level 12 (Android 3.1) から

・int updatePeriodMillis - android:updatePeriodMillis
  (AppWidget meta-data file)
  注意:30分に配送されるアップデートリクエストは1回まで


public class AppWidgetManagerTestActivity extends Activity {

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

AppWidgetManager manager = AppWidgetManager.getInstance(this);

List widgetList = manager.getInstalledProviders();

for(AppWidgetProviderInfo info : widgetList) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
Log.d("autoAdvanceViewId", info.autoAdvanceViewId + "");

if(info.configure != null)
Log.d("configure", info.configure.flattenToString());

Log.d("icon", info.icon + "");
Log.d("initialLayout", info.initialLayout + "");
Log.d("label", info.label);
Log.d("minHeight", info.minHeight + "");
Log.d("minWidth", info.minWidth + "");

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
Log.d("previewImage", info.previewImage + "");

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1)
Log.d("resizeMode", info.resizeMode + "");

Log.d("updatePeriodMilis", info.updatePeriodMillis + "");

if(info.provider != null)
Log.d("provider", info.provider.flattenToString());
}
}
}




Android 2.3.4 の Emulator

D/configure( 682): com.android.quicksearchbox/com.android.quicksearchbox.SearchWidgetConfigActivity
D/icon ( 682): 2130837545
D/initialLayout( 682): 2130903050
D/label ( 682): 検索
D/minHeight( 682): 18433
D/minWidth( 682): 75265
D/updatePeriodMilis( 682): 0
D/provider( 682): com.android.quicksearchbox/com.android.quicksearchbox.SearchWidgetProvider

D/icon ( 682): 2130837507
D/initialLayout( 682): 2130903040
D/label ( 682): 音楽
D/minHeight( 682): 18433
D/minWidth( 682): 75265
D/updatePeriodMilis( 682): 0
D/provider( 682): com.android.music/com.android.music.MediaAppWidgetProvider

D/configure( 682): com.android.gallery/com.android.camera.PhotoAppWidgetConfigure
D/icon ( 682): 2130837549
D/initialLayout( 682): 2130903049
D/label ( 682): 写真フレーム
D/minHeight( 682): 37377
D/minWidth( 682): 37377
D/updatePeriodMilis( 682): 0
D/provider( 682): com.android.gallery/com.android.camera.PhotoAppWidgetProvider

D/icon ( 682): 2130837544
D/initialLayout( 682): 2130903102
D/label ( 682): 電源管理
D/minHeight( 682): 18433
D/minWidth( 682): 75265
D/updatePeriodMilis( 682): 0
D/provider( 682): com.android.settings/com.android.settings.widget.SettingsAppWidgetProvider

D/icon ( 682): 2130837551
D/initialLayout( 682): 2130903043
D/label ( 682): アナログ時計
D/minHeight( 682): 37377
D/minWidth( 682): 37377
D/updatePeriodMilis( 682): 0
D/provider( 682): com.android.deskclock/com.android.alarmclock.AnalogAppWidgetProvider

D/icon ( 682): 2130837513
D/initialLayout( 682): 2130903042
D/label ( 682): ホーム画面のヒント
D/minHeight( 682): 18433
D/minWidth( 682): 75265
D/updatePeriodMilis( 682): 0
D/provider( 682): com.android.protips/com.android.protips.ProtipWidget


android:configure は最初に widget を配置したときに呼び出される設定用の Activity です。
検索 Widget では検索対象を選択するためのリストダイアログが、写真フレーム Widget では表示する写真を選ぶ Activity が開始するようになっています。

minHeight と minWidth は AppWidget の dp単位での縦横幅なんですけど
 72dp = 18433
 146dp = 37377
 294dp = 75265
のように、256倍して 1 足した値になってました。




---
AppWidget の作り方

AppWidget アプリを作るのに必要なのが

 ・AppWidgetProviderInfo オブジェクト
   通常は res/xml/hoge.xml に <appwidget-provider>
   エレメントを使って記述する

 ・AppWidgetProvider の実装クラス
   AppWidgetProvider クラスを継承した独自クラスを作る

 ・AppWidget の View レイアウト
   res/layout/ に XML で定義する

の3つです。

必要に応じて
 ・App Widget 用の Configuration Activity
   AppWidget の追加時起動される Activity で、
   生成時にユーザーが App Widget の設定を
   できるようにするためのもの

も用意します。

1. App Widget を Manifest に宣言する

AndroidManifest.xml に AppWidgetProvider を継承した独自クラスを宣言します。

<receiver android:name=".HelloAppWidgetProvider"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/appwidget"/>
</receiver>


このクラスが ACTION_APPWIDGET_UPDATE broadcast を受け取れるように <intent-filter> で指定します。
ここで指定しなければならない Action はこれだけで、他の ACTION_APPWIDGET_DELETED や ACTION_APPWIDGET_ENABLED などの broadcast は AppWidgetManager が必要に応じて自動的に AppWidgetProvider に送ってくれます。


2. AppWidgetProviderInfo Metadata を追加する

AppWidgetProviderInfo は最小レイアウトサイズ、初期レイアウトレソース、どのくらいの頻度で AppWidget を更新するか、生成時に起動する設定Activity など AppWidget の基礎情報を定義します。AppWidgetProviderInfo オブジェクトを定義するには、XML ファイルで <appwidget-provider> エレメントを使って宣言し res/xml/ フォルダ内に保存します。


<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="294dp"
android:minHeight="72dp"
android:updatePeriodMillis="86400000"
android:previewImage="@drawable/preview"
android:initialLayout="@layout/example_appwidget"
android:configure="com.example.android.ExampleAppWidgetConfigure"
android:resizeMode="horizontal|vertical">
</appwidget-provider>

* android:previewImage は API Level 11 から、android:resizeMode は API Level 12 からです。

android:minWidthandroid:minHeight に指定するサイズは次の式が推奨されています。
(number of cells * 74) - 2 [dp]

android:updatePeriodMillis にはどのくらいの頻度で AppWidgetProvider から App Widget framework に update を依頼するかを宣言します。
86400000 は1日のミリ秒数なので、この場合は1日に1回 update のリクエストをするということです。頻繁なアップデートは電池を消耗するため、少なくとも 1時間に1回以上は行わないことが推奨されています。もしくはユーザーが更新頻度を設定できるようにするもの1つの方法ですし、updatePeriodMillis を使わずに AlarmManager や Service で Intent を投げる方法もあります。特に updatePeriodMillis は update 時にデバイスがスリープ状態だと、update のためにスリープ状態から抜けます。起動時にのみ更新したい場合は AlarmManager や Service を使いましょう。

android:previewImage は Android 3.0 (API Level 11) で追加された属性で、ユーザーが追加する AppWidget を選択するときに、実際に追加したときの見た目を確認できるようにするためのものです。指定されていない場合は通常のランチャーアイコンが表示されます。

android:autoAdvanceViewId 属性は Android 3.0 (API Level 11) で追加された属性で、AppWidget subview の view ID を指定します。AppWidget subview は widget のホストによって自動切り替えされるものです。

android:resizeMode 属性は Android 3.1 (API Level 12) で追加された属性で、Widget がリサイズできるかどうかのルールを指定します。指定できる値は "horizontal", "vertical", "none", "horizontal|vertical" です。


3. App Widget のレイアウトを作成する

通常のアプリのレイアウトと同様に res/layout/ フォルダ内に XML で定義します。
ただし、App Widget のレイアウトは RemoteViews を基づいているため、すべてのレイアウトや View Widget をサポートしているわけではありません。

RemoteView がサポートできるは、次のレイアウトクラスと

 ・FrameLayout
 ・LinearLayout
 ・RelativeLayout

Widget クラスだけです

 ・AnalogClock
 ・Button
 ・Chronometer
 ・ImageButton
 ・ImageView
 ・ProgressBar
 ・TextView
 ・ViewFlipper

これらのクラスを継承したクラスはサポート外です。


4. AppWidgetProvider クラスを使う

AppWidgetProvider を継承したクラスを作ります。
この AppWidgetProvider クラスには次の5つの public mothod があり、このうち onReceive が AppWidgetProvider が継承している BroadcastReceiver からきたもの。それ以外が AppWidgetProvider 独自のものです。
 ・onReceive(Context context, Intent intent)

 ・onEnabled(Context context)
   最初の AppWidget が追加されるときに呼ばれる

 ・onUpdate(Context context,
   AppWidgetManager appWidgetManager,
   int[] appWidgetIds)

   AppWidget が追加されるとき、もしくは updatePeriodMillis
   で指定されたインターバルに達したときに呼ばれる

 ・onDisabled(Context context)
   App Widget の最後のインスタンスが削除されたときに呼ばれる

 ・onDeleted(Context context, int[] appWidgetIds)
   App Widget のインスタンスが削除されたときに呼ばれる

AppWidgetManager が各 AppWidget に対する操作に応じた Intent をなげます。
上記のメソッドはそれらに応じて呼ばれるのですが、実際は onReceive が Intent を一括して受け取って処理しています。
ちょっと AppWidgetProvider のコードを見てましょう。そんなに長くありません。

http://tools.oesf.biz/android-2.3_r1.0/xref/frameworks/base/core/java/android/appwidget/AppWidgetProvider.java

public class AppWidgetProvider extends BroadcastReceiver {
public AppWidgetProvider() {
}

public void onReceive(Context context, Intent intent) {
// Protect against rogue update broadcasts (not really a security issue,
// just filter bad broacasts out so subclasses are less likely to crash).
String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null) {
int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if (appWidgetIds != null && appWidgetIds.length > 0) {
this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
}
}
}
else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
this.onDeleted(context, new int[] { appWidgetId });
}
}
else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
this.onEnabled(context);
}
else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
this.onDisabled(context);
}
}
// END_INCLUDE(onReceive)

public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
}

public void onDeleted(Context context, int[] appWidgetIds) {
}

public void onEnabled(Context context) {
}

public void onDisabled(Context context) {
}
}


ようは、onReceive で受け取った Intent の Action 応じて onUpdate, onDelete, onEnabled, onDisabled のいずれかを呼んでいるだけです。

その際、ACTION_APPWIDGET_UPDATE の Intent や ACTION_APPWIDGET_DELETED の Intent から AppWidgetId を取り出して引数で渡してくれています。

これをみると ACTION_APPWIDGET_UPDATE では複数の ID が EXTRA_APPWIDGET_IDS に、ACTION_APPWIDGET_DELETED では1つの ID が EXTRA_APPWIDGET_ID に入っていることがわかります。onDeleted に渡される appWidgetIds には必ず1つしか含まれないのに配列になっているのは onUpdate との対称性を持たせるためでしょうかね。


AppWidget 上の View に ClickListener を設定する処理等は onUpdate で行うのが普通です。


public class ExampleAppWidgetProvider extends AppWidgetProvider {

public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int N = appWidgetIds.length;

// Perform this loop procedure for each App Widget that belongs to this provider
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];

// Create an Intent to launch ExampleActivity
Intent intent = new Intent(context, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

// Get the layout for the App Widget and attach an on-click listener
// to the button
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);

// Tell the AppWidgetManager to perform an update on the current app widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
}


onUpdate ではネットワークにアクセスするなどの時間のかかる処理を行うべきではありません。それは Application Not Responding (ANR) によって AppWidgetProvider が閉じる要因になりえます。onUpdate() 内で Service を起動して Service 内で必要な処理をするようにしましょう。
Service を使った AppWidget のサンプルが Wikitionary Sample's AppWidgetProvider です。

AppWigetProvider を利用せずに BroadcastReceiver を継承して直接 Intent を受け取って処理することもできます。その場合は次の4つの Intent を受け取るようにし、onReceive(Context, Intent) を override します。

 ・ACTION_APPWIDGET_UPDATE
 ・ACTION_APPWIDGET_DELETED
 ・ACTION_APPWIDGET_ENABLED
 ・ACTION_APPWIDGET_DISABLED



5. App Widget 設定 Activity を作成する (オプション)

先ほども出てきた ウィジェット用の設定画面を指定する android:configure ですが、ウィジェットの追加時に呼ばれるので、同じアプリの AppWidget の2個目を追加するときでも呼ばれます。AppWidget の起動時に自動で呼ばれるので、Widget の色やサイズなどをユーザーに選択させることができます。

android:configure には、絶対パスで指定します。


android:configure="yanzm.example.appwidget.hello.HelloAppWidgetConfigure"


もちろん android:configure に指定する Activity も Manifest に指定しておく必要があります。
このとき、android.appwidget.action.APPWIDGET_CONFIGURE に対応するようにしておきます。


<activity android:name=".HelloAppWidgetConfigure">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>


この Activity では呼び出し元の Activity に結果を返さなければいけません。
RESULT_CANCEL を返した場合、AppWidget は追加されません。
RESULT_OK を返した場合に AppWidget が追加されるのですが、Extras に WidgetId を入れて返さなければなりません。
通常は onCreate で Intent から渡された WidgetId を取り出して保持しておき、Activity を終了するときにそれを返すようにします。
(EXTRA_APPWIDGET_ID という extra にいれる)


public class HelloAppWidgetConfigure extends Activity {

int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;

public void onCreate(Bundle icicle) {
super.onCreate(icicle);

setResult(RESULT_CANCELED);

setContentView(R.layout.main);

// Find the widget id from the intent.
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null) {
mAppWidgetId = extras.getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
}

// If they gave us an intent without the widget id, just bail.
if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
finish();
}
}

...

private void finishConfigure() {
// Push widget update to surface with newly set prefix
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ExampleAppWidgetProvider.updateAppWidget(context, appWidgetManager,
mAppWidgetId, titlePrefix);

// Make sure we pass back the original appWidgetId
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
// setResult(RESULT_OK); だとランチャーアプリが落ちることがある
}
}



もう1つ重要な注意点が、Configuration Activity が起動した場合 ACTION_APPWIDGET_UPDATE が broadcast されない = onUpdate() メソッドが呼ばれない、という点です。Configuration Activity が AppWidgetManager に対して update のリクエストを行う責任を持ちます。これ以降は onUpdate() は呼ばれます。スキップされるのは最初だけということです。

App Widget を Configuration Activity からアップデートするには AppWidgetManager を使って次のようします。

  1. Activity を起動した Intent から App Widget ID を取得する

Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null) {
mAppWidgetId = extras.getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
}


  2. App Widget の設定を行う

  3. 設定が終わったら、getInstance(Context) を呼んで
   AppWigetManager のインスタンスを取得する

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);


  4. updateAppWidget(int, RemoteViews) を呼んで
   RemoteViews レイアウトの App Widget をアップデートする

RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.example_appwidget);
appWidgetManager.updateAppWidget(mAppWidgetId, views);

2011年6月12日日曜日

DateUtils で1日の miliseconds を取得する

一日のミリ秒数が欲しいとき、DateUtils の定数を利用できます。

long YEAR_IN_MILLIS : 31449600000
 注意: これは 364 日の長さで、1年ではありません。

long WEEK_IN_MILLIS : 604800000

long DAY_IN_MILLIS : 86400000

long HOUR_IN_MILLIS : 3600000

long MINUTE_IN_MILLIS : 60000

long SECOND_IN_MILLIS : 1000

2011年6月5日日曜日

Android 日時の表示形式を指定する

DateUtils.formatDateTime (Context context, long millis, int flags)

DateUtils.formatDateRange (Context context, Formatter formatter, long startMillis, long endMillis, int flags, String timeZone)

DateUtils.formatDateRange (Context context, long startMillis, long endMillis, int flags)

DateUtils.formatDateRange (Context context, Formatter formatter, long startMillis, long endMillis, int flags)

の flag に指定するパラメータ
| で組み合わせる


■表示する要素を指定

 DateUtils.FORMAT_SHOW_TIME 時刻を表示
 DateUtils.FORMAT_SHOW_DATE 日付を表示
 DateUtils.FORMAT_SHOW_WEEKDAY 曜日を表示
 DateUtils.FORMAT_SHOW_YEAR 年を表示
 DateUtils.FORMAT_NUMERIC_DATE 
 DateUtils.FORMAT_UTC

■時刻に関するフォーマット

 DateUtils.FORMAT_12HOUR
 DateUtils.FORMAT_24HOUR
 DateUilts.FORMAT_CAP_AMPM
 DateUilts.FORMAT_CAP_MIDNIGHT
 DateUilts.FORMAT_CAP_NOON
 DateUilts.FORMAT_CAP_NONN_MIDNIGHT
 DateUilts.FORMAT_ABBREV_TIME
 DateUilts.FORMAT_NO_NOON
 DateUilts.FORMAT_NO_MIDNIGHT
 DateUilts.FORMAT_NO_NOON_MIDNIGHT

■日付に関するフォーマット

 DateUilts.FORMAT_ABBREV_MONTH

■曜日に関するフォーマット

 DateUtils.FORMAT_ABBREV_WEEKDAY



int[] TIME_FLAGS = {
DateUtils.FORMAT_SHOW_TIME,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_12HOUR,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_CAP_AMPM,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_CAP_MIDNIGHT,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_CAP_NOON,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_CAP_NOON_MIDNIGHT,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_TIME,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_NO_NOON,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_NO_MIDNIGHT,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_NO_NOON_MIDNIGHT,
};

int[] DATE_FLAGS = {
DateUtils.FORMAT_SHOW_DATE,
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH,
DateUtils.FORMAT_NUMERIC_DATE,
DateUtils.FORMAT_UTC,
};

int[] WEEKDAY_FLAGS = {
DateUtils.FORMAT_SHOW_WEEKDAY,
DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_WEEKDAY,
};

int[] COMBINE_FLAGS = {
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_WEEKDAY,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR |
DateUtils.FORMAT_ABBREV_ALL,
};


英語 Location

日本語 Location



コード全部はこんな感じ

package yanzm.example.sampletest;

import android.app.Activity;
import android.os.Bundle;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;

public class SampleTestActivity extends Activity {
private static Time sTime = new Time();

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

int[] TIME_FLAGS = {
DateUtils.FORMAT_SHOW_TIME,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_12HOUR,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_CAP_AMPM,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_CAP_MIDNIGHT,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_CAP_NOON,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_CAP_NOON_MIDNIGHT,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_TIME,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_NO_NOON,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_NO_MIDNIGHT,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_NO_NOON_MIDNIGHT,
};

int[] DATE_FLAGS = {
DateUtils.FORMAT_SHOW_DATE,
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH,
DateUtils.FORMAT_NUMERIC_DATE,
DateUtils.FORMAT_UTC,
};

int[] WEEKDAY_FLAGS = {
DateUtils.FORMAT_SHOW_WEEKDAY,
DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_WEEKDAY,
};

int[] COMBINE_FLAGS = {
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_ABBREV_ALL,
};

long TIME1 = parseTime("2011-07-17T14:00:00.000+09:00");
long TIME2 = parseTime("2011-07-17T12:00:00.000+09:00");
long TIME3 = parseTime("2011-07-17T00:00:00.000+09:00");

for(int i = 0; i < TIME_FLAGS.length; i++){
TableRow tr = new TableRow(this);
TextView tv;
tv = new TextView(this);
tv.setPadding(0, 0, 40, 0);
tv.setText(DateUtils.formatDateTime(this, TIME1, TIME_FLAGS[i]));
tr.addView(tv);
tv = new TextView(this);
tv.setText(DateUtils.formatDateTime(this, TIME2, TIME_FLAGS[i]));
tr.addView(tv);
tv = new TextView(this);
tv.setPadding(40, 0, 0, 0);
tv.setText(DateUtils.formatDateTime(this, TIME3, TIME_FLAGS[i]));
tr.addView(tv);

TableLayout tl = (TableLayout)findViewById(R.id.time);
tl.addView(tr);
}

long DATE1 = parseTime("2011-07-17T14:00:00.000+09:00");
long DATE2 = parseTime("2011-10-05T14:00:00.000+09:00");

for(int i = 0; i < DATE_FLAGS.length; i++){
TableRow tr = new TableRow(this);
TextView tv;
tv = new TextView(this);
tv.setPadding(0, 0, 40, 0);
tv.setText(DateUtils.formatDateTime(this, DATE1, DATE_FLAGS[i]));
tr.addView(tv);
tv = new TextView(this);
tv.setText(DateUtils.formatDateTime(this, DATE2, DATE_FLAGS[i]));
tr.addView(tv);

TableLayout tl = (TableLayout)findViewById(R.id.date);
tl.addView(tr);
}

for(int i = 0; i < WEEKDAY_FLAGS.length; i++){
TableRow tr = new TableRow(this);
TextView tv;
tv = new TextView(this);
tv.setPadding(0, 0, 40, 0);
tv.setText(DateUtils.formatDateTime(this, DATE1, WEEKDAY_FLAGS[i]));
tr.addView(tv);
tv = new TextView(this);
tv.setText(DateUtils.formatDateTime(this, DATE2, WEEKDAY_FLAGS[i]));
tr.addView(tv);

TableLayout tl = (TableLayout)findViewById(R.id.weekday);
tl.addView(tr);
}

for(int i = 0; i < COMBINE_FLAGS.length; i++){
TableRow tr = new TableRow(this);
TextView tv;
tv = new TextView(this);
tv.setText(DateUtils.formatDateTime(this, DATE1, COMBINE_FLAGS[i]));
tr.addView(tv);

TableLayout tl = (TableLayout)findViewById(R.id.combine);
tl.addView(tr);
}
}

public static long parseTime(String time) {
sTime.parse3339(time);
return sTime.toMillis(false);
}
}

2011年6月4日土曜日

TimeZone Id の取得

TimeZone の ID 一覧を取得するには getAvailableIDs() を使う。

この ID から TimeZone を取得するには getTimeZone(String id) を使う。

日本なら、Asia/Tokyo か Japan かな。


String[] ids = TimeZone.getAvailableIDs();

for(String id : ids) {
TimeZone tz = TimeZone.getTimeZone(id);
Log.d("ID", id +
"\t" + tz.getDisplayName() +
"\t" + tz.getDisplayName(false, TimeZone.SHORT));
}


More...

D/ID ( 4228): Africa/Abidjan グリニッジ標準時 GMT+00:00

D/ID ( 4228): Africa/Accra グリニッジ標準時 GMT+00:00

D/ID ( 4228): Africa/Addis_Ababa 東アフリカ時間 GMT+03:00

D/ID ( 4228): Africa/Algiers 中欧標準時 GMT+01:00

D/ID ( 4228): Africa/Asmara 東アフリカ時間 GMT+03:00

D/ID ( 4228): Africa/Asmera 東アフリカ時間 GMT+03:00

D/ID ( 4228): Africa/Bamako グリニッジ標準時 GMT+00:00

D/ID ( 4228): Africa/Bangui 西アフリカ時間 GMT+01:00

D/ID ( 4228): Africa/Banjul グリニッジ標準時 GMT+00:00

D/ID ( 4228): Africa/Bissau グリニッジ標準時 GMT+00:00

D/ID ( 4228): Africa/Blantyre 中央アフリカ時間 GMT+02:00

D/ID ( 4228): Africa/Brazzaville 西アフリカ時間 GMT+01:00

D/ID ( 4228): Africa/Bujumbura 中央アフリカ時間 GMT+02:00

D/ID ( 4228): Africa/Cairo 東欧標準時 GMT+02:00

D/ID ( 4228): Africa/Casablanca GMT+00:00 GMT+00:00

D/ID ( 4228): Africa/Ceuta 中欧標準時 GMT+01:00

D/ID ( 4228): Africa/Conakry グリニッジ標準時 GMT+00:00

D/ID ( 4228): Africa/Dakar グリニッジ標準時 GMT+00:00

D/ID ( 4228): Africa/Dar_es_Salaam 東アフリカ時間 GMT+03:00

D/ID ( 4228): Africa/Djibouti 東アフリカ時間 GMT+03:00

D/ID ( 4228): Africa/Douala 西アフリカ時間 GMT+01:00

D/ID ( 4228): Africa/El_Aaiun GMT+00:00 GMT+00:00

D/ID ( 4228): Africa/Freetown グリニッジ標準時 GMT+00:00

D/ID ( 4228): Africa/Gaborone 中央アフリカ時間 GMT+02:00

D/ID ( 4228): Africa/Harare 中央アフリカ時間 GMT+02:00

D/ID ( 4228): Africa/Johannesburg 南アフリカ標準時 GMT+02:00

D/ID ( 4228): Africa/Kampala 東アフリカ時間 GMT+03:00

D/ID ( 4228): Africa/Khartoum 東アフリカ時間 GMT+03:00

D/ID ( 4228): Africa/Kigali 中央アフリカ時間 GMT+02:00

D/ID ( 4228): Africa/Kinshasa 西アフリカ時間 GMT+01:00

D/ID ( 4228): Africa/Lagos 西アフリカ時間 GMT+01:00

D/ID ( 4228): Africa/Libreville 西アフリカ時間 GMT+01:00

D/ID ( 4228): Africa/Lome グリニッジ標準時 GMT+00:00

D/ID ( 4228): Africa/Luanda 西アフリカ時間 GMT+01:00

D/ID ( 4228): Africa/Lubumbashi 中央アフリカ時間 GMT+02:00

D/ID ( 4228): Africa/Lusaka 中央アフリカ時間 GMT+02:00

D/ID ( 4228): Africa/Malabo 西アフリカ時間 GMT+01:00

D/ID ( 4228): Africa/Maputo 中央アフリカ時間 GMT+02:00

D/ID ( 4228): Africa/Maseru 南アフリカ標準時 GMT+02:00

D/ID ( 4228): Africa/Mbabane 南アフリカ標準時 GMT+02:00

D/ID ( 4228): Africa/Mogadishu 東アフリカ時間 GMT+03:00

D/ID ( 4228): Africa/Monrovia グリニッジ標準時 GMT+00:00

D/ID ( 4228): Africa/Nairobi 東アフリカ時間 GMT+03:00

D/ID ( 4228): Africa/Ndjamena 西アフリカ時間 GMT+01:00

D/ID ( 4228): Africa/Niamey 西アフリカ時間 GMT+01:00

D/ID ( 4228): Africa/Nouakchott グリニッジ標準時 GMT+00:00

D/ID ( 4228): Africa/Ouagadougou グリニッジ標準時 GMT+00:00

D/ID ( 4228): Africa/Porto-Novo 西アフリカ時間 GMT+01:00

D/ID ( 4228): Africa/Sao_Tome グリニッジ標準時 GMT+00:00

D/ID ( 4228): Africa/Timbuktu グリニッジ標準時 GMT+00:00

D/ID ( 4228): Africa/Tripoli 東欧標準時 GMT+02:00

D/ID ( 4228): Africa/Tunis 中欧標準時 GMT+01:00

D/ID ( 4228): Africa/Windhoek 西アフリカ時間 GMT+01:00

D/ID ( 4228): America/Adak GMT-10:00 GMT-10:00

D/ID ( 4228): America/Anchorage アラスカ標準時 GMT-09:00

D/ID ( 4228): America/Anguilla 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Antigua 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Araguaina ブラジリア時間 GMT-03:00

D/ID ( 4228): America/Argentina/Buenos_Aires GMT-03:00 GMT-03:00

D/ID ( 4228): America/Argentina/Catamarca GMT-03:00 GMT-03:00

D/ID ( 4228): America/Argentina/ComodRivadavia GMT-03:00 GMT-03:00

D/ID ( 4228): America/Argentina/Cordoba GMT-03:00 GMT-03:00

D/ID ( 4228): America/Argentina/Jujuy GMT-03:00 GMT-03:00

D/ID ( 4228): America/Argentina/La_Rioja GMT-03:00 GMT-03:00

D/ID ( 4228): America/Argentina/Mendoza GMT-03:00 GMT-03:00

D/ID ( 4228): America/Argentina/Rio_Gallegos GMT-03:00 GMT-03:00

D/ID ( 4228): America/Argentina/Salta GMT-03:00 GMT-03:00

D/ID ( 4228): America/Argentina/San_Juan GMT-03:00 GMT-03:00

D/ID ( 4228): America/Argentina/San_Luis GMT-04:00 GMT-04:00

D/ID ( 4228): America/Argentina/Tucuman GMT-03:00 GMT-03:00

D/ID ( 4228): America/Argentina/Ushuaia GMT-03:00 GMT-03:00

D/ID ( 4228): America/Aruba 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Asuncion GMT-04:00 GMT-04:00

D/ID ( 4228): America/Atikokan アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Atka GMT-10:00 GMT-10:00

D/ID ( 4228): America/Bahia ブラジリア時間 GMT-03:00

D/ID ( 4228): America/Bahia_Banderas GMT+00:00 GMT+00:00

D/ID ( 4228): America/Barbados 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Belem ブラジリア時間 GMT-03:00

D/ID ( 4228): America/Belize アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Blanc-Sablon 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Boa_Vista アマゾン時間 GMT-04:00

D/ID ( 4228): America/Bogota GMT-05:00 GMT-05:00

D/ID ( 4228): America/Boise アメリカ山地標準時 GMT-07:00

D/ID ( 4228): America/Buenos_Aires GMT-03:00 GMT-03:00

D/ID ( 4228): America/Cambridge_Bay アメリカ山地標準時 GMT-07:00

D/ID ( 4228): America/Campo_Grande アマゾン時間 GMT-04:00

D/ID ( 4228): America/Cancun アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Caracas GMT-04:30 GMT-04:30

D/ID ( 4228): America/Catamarca GMT-03:00 GMT-03:00

D/ID ( 4228): America/Cayenne GMT-03:00 GMT-03:00

D/ID ( 4228): America/Cayman アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Chicago アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Chihuahua アメリカ山地標準時 GMT-07:00

D/ID ( 4228): America/Coral_Harbour アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Cordoba GMT-03:00 GMT-03:00

D/ID ( 4228): America/Costa_Rica アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Cuiaba アマゾン時間 GMT-04:00

D/ID ( 4228): America/Curacao 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Danmarkshavn グリニッジ標準時 GMT+00:00

D/ID ( 4228): America/Dawson アメリカ太平洋標準時 GMT-08:00

D/ID ( 4228): America/Dawson_Creek アメリカ山地標準時 GMT-07:00

D/ID ( 4228): America/Denver アメリカ山地標準時 GMT-07:00

D/ID ( 4228): America/Detroit アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Dominica 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Edmonton アメリカ山地標準時 GMT-07:00

D/ID ( 4228): America/Eirunepe GMT-05:00 GMT-05:00

D/ID ( 4228): America/El_Salvador アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Ensenada アメリカ太平洋標準時 GMT-08:00

D/ID ( 4228): America/Fort_Wayne アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Fortaleza ブラジリア時間 GMT-03:00

D/ID ( 4228): America/Glace_Bay 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Godthab GMT-03:00 GMT-03:00

D/ID ( 4228): America/Goose_Bay 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Grand_Turk アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Grenada 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Guadeloupe 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Guatemala アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Guayaquil GMT-05:00 GMT-05:00

D/ID ( 4228): America/Guyana GMT-04:00 GMT-04:00

D/ID ( 4228): America/Halifax 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Havana GMT-05:00 GMT-05:00

D/ID ( 4228): America/Hermosillo アメリカ山地標準時 GMT-07:00

D/ID ( 4228): America/Indiana/Indianapolis アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Indiana/Knox アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Indiana/Marengo アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Indiana/Petersburg アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Indiana/Tell_City アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Indiana/Vevay アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Indiana/Vincennes アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Indiana/Winamac アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Indianapolis アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Inuvik アメリカ山地標準時 GMT-07:00

D/ID ( 4228): America/Iqaluit アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Jamaica アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Jujuy GMT-03:00 GMT-03:00

D/ID ( 4228): America/Juneau アラスカ標準時 GMT-09:00

D/ID ( 4228): America/Kentucky/Louisville アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Kentucky/Monticello アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Knox_IN アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/La_Paz GMT-04:00 GMT-04:00

D/ID ( 4228): America/Lima GMT-05:00 GMT-05:00

D/ID ( 4228): America/Los_Angeles アメリカ太平洋標準時 GMT-08:00

D/ID ( 4228): America/Louisville アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Maceio ブラジリア時間 GMT-03:00

D/ID ( 4228): America/Managua アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Manaus アマゾン時間 GMT-04:00

D/ID ( 4228): America/Marigot 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Martinique 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Matamoros アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Mazatlan アメリカ山地標準時 GMT-07:00

D/ID ( 4228): America/Mendoza GMT-03:00 GMT-03:00

D/ID ( 4228): America/Menominee アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Merida アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Mexico_City アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Miquelon GMT-03:00 GMT-03:00

D/ID ( 4228): America/Moncton 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Monterrey アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Montevideo GMT-03:00 GMT-03:00

D/ID ( 4228): America/Montreal アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Montserrat 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Nassau アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/New_York アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Nipigon アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Nome アラスカ標準時 GMT-09:00

D/ID ( 4228): America/Noronha GMT-02:00 GMT-02:00

D/ID ( 4228): America/North_Dakota/Center アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/North_Dakota/New_Salem アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Ojinaga アメリカ山地標準時 GMT-07:00

D/ID ( 4228): America/Panama アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Pangnirtung アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Paramaribo GMT-03:00 GMT-03:00

D/ID ( 4228): America/Phoenix アメリカ山地標準時 GMT-07:00

D/ID ( 4228): America/Port-au-Prince アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Port_of_Spain 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Porto_Acre GMT-05:00 GMT-05:00

D/ID ( 4228): America/Porto_Velho アマゾン時間 GMT-04:00

D/ID ( 4228): America/Puerto_Rico 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Rainy_River アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Rankin_Inlet アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Recife ブラジリア時間 GMT-03:00

D/ID ( 4228): America/Regina アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Resolute アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Rio_Branco GMT-05:00 GMT-05:00

D/ID ( 4228): America/Rosario GMT-03:00 GMT-03:00

D/ID ( 4228): America/Santa_Isabel アメリカ太平洋標準時 GMT-08:00

D/ID ( 4228): America/Santarem アマゾン時間 GMT-04:00

D/ID ( 4228): America/Santiago GMT-04:00 GMT-04:00

D/ID ( 4228): America/Santo_Domingo 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Sao_Paulo ブラジリア時間 GMT-03:00

D/ID ( 4228): America/Scoresbysund GMT-01:00 GMT-01:00

D/ID ( 4228): America/Shiprock アメリカ山地標準時 GMT-07:00

D/ID ( 4228): America/St_Barthelemy 大西洋標準時 GMT-04:00

D/ID ( 4228): America/St_Johns ニューファンドランド島標準時 GMT-03:30

D/ID ( 4228): America/St_Kitts 大西洋標準時 GMT-04:00

D/ID ( 4228): America/St_Lucia 大西洋標準時 GMT-04:00

D/ID ( 4228): America/St_Thomas 大西洋標準時 GMT-04:00

D/ID ( 4228): America/St_Vincent 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Swift_Current アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Tegucigalpa アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Thule 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Thunder_Bay アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Tijuana アメリカ太平洋標準時 GMT-08:00

D/ID ( 4228): America/Toronto アメリカ東部標準時 GMT-05:00

D/ID ( 4228): America/Tortola 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Vancouver アメリカ太平洋標準時 GMT-08:00

D/ID ( 4228): America/Virgin 大西洋標準時 GMT-04:00

D/ID ( 4228): America/Whitehorse アメリカ太平洋標準時 GMT-08:00

D/ID ( 4228): America/Winnipeg アメリカ中部標準時 GMT-06:00

D/ID ( 4228): America/Yakutat アラスカ標準時 GMT-09:00

D/ID ( 4228): America/Yellowknife アメリカ山地標準時 GMT-07:00

D/ID ( 4228): Antarctica/Casey GMT+08:00 GMT+08:00

D/ID ( 4228): Antarctica/Davis GMT+07:00 GMT+07:00

D/ID ( 4228): Antarctica/DumontDUrville GMT+10:00 GMT+10:00

D/ID ( 4228): Antarctica/Macquarie GMT+10:00 GMT+10:00

D/ID ( 4228): Antarctica/Mawson GMT+06:00 GMT+06:00

D/ID ( 4228): Antarctica/McMurdo GMT+12:00 GMT+12:00

D/ID ( 4228): Antarctica/Palmer GMT-04:00 GMT-04:00

D/ID ( 4228): Antarctica/Rothera GMT-03:00 GMT-03:00

D/ID ( 4228): Antarctica/South_Pole GMT+12:00 GMT+12:00

D/ID ( 4228): Antarctica/Syowa GMT+03:00 GMT+03:00

D/ID ( 4228): Antarctica/Vostok GMT+06:00 GMT+06:00

D/ID ( 4228): Arctic/Longyearbyen 中欧標準時 GMT+01:00

D/ID ( 4228): Asia/Aden GMT+03:00 GMT+03:00

D/ID ( 4228): Asia/Almaty GMT+06:00 GMT+06:00

D/ID ( 4228): Asia/Amman 東欧標準時 GMT+02:00

D/ID ( 4228): Asia/Anadyr GMT+12:00 GMT+12:00

D/ID ( 4228): Asia/Aqtau GMT+05:00 GMT+05:00

D/ID ( 4228): Asia/Aqtobe GMT+05:00 GMT+05:00

D/ID ( 4228): Asia/Ashgabat GMT+05:00 GMT+05:00

D/ID ( 4228): Asia/Ashkhabad GMT+05:00 GMT+05:00

D/ID ( 4228): Asia/Baghdad GMT+03:00 GMT+03:00

D/ID ( 4228): Asia/Bahrain GMT+03:00 GMT+03:00

D/ID ( 4228): Asia/Baku GMT+04:00 GMT+04:00

D/ID ( 4228): Asia/Bangkok GMT+07:00 GMT+07:00

D/ID ( 4228): Asia/Beirut 東欧標準時 GMT+02:00

D/ID ( 4228): Asia/Bishkek GMT+06:00 GMT+06:00

D/ID ( 4228): Asia/Brunei GMT+08:00 GMT+08:00

D/ID ( 4228): Asia/Calcutta GMT+05:30 GMT+05:30

D/ID ( 4228): Asia/Choibalsan GMT+09:00 GMT+09:00

D/ID ( 4228): Asia/Chongqing 中国標準時 GMT+08:00

D/ID ( 4228): Asia/Chungking 中国標準時 GMT+08:00

D/ID ( 4228): Asia/Colombo GMT+05:30 GMT+05:30

D/ID ( 4228): Asia/Dacca GMT+06:00 GMT+06:00

D/ID ( 4228): Asia/Damascus 東欧標準時 GMT+02:00

D/ID ( 4228): Asia/Dhaka GMT+06:00 GMT+06:00

D/ID ( 4228): Asia/Dili GMT+09:00 GMT+09:00

D/ID ( 4228): Asia/Dubai GMT+04:00 GMT+04:00

D/ID ( 4228): Asia/Dushanbe GMT+05:00 GMT+05:00

D/ID ( 4228): Asia/Gaza 東欧標準時 GMT+02:00

D/ID ( 4228): Asia/Harbin 中国標準時 GMT+08:00

D/ID ( 4228): Asia/Ho_Chi_Minh GMT+07:00 GMT+07:00

D/ID ( 4228): Asia/Hong_Kong GMT+08:00 GMT+08:00

D/ID ( 4228): Asia/Hovd GMT+07:00 GMT+07:00

D/ID ( 4228): Asia/Irkutsk GMT+08:00 GMT+08:00

D/ID ( 4228): Asia/Istanbul 東欧標準時 GMT+02:00

D/ID ( 4228): Asia/Jakarta GMT+07:00 GMT+07:00

D/ID ( 4228): Asia/Jayapura GMT+09:00 GMT+09:00

D/ID ( 4228): Asia/Jerusalem イスラエル標準時 GMT+02:00

D/ID ( 4228): Asia/Kabul GMT+04:30 GMT+04:30

D/ID ( 4228): Asia/Kamchatka GMT+12:00 GMT+12:00

D/ID ( 4228): Asia/Karachi GMT+05:00 GMT+05:00

D/ID ( 4228): Asia/Kashgar 中国標準時 GMT+08:00

D/ID ( 4228): Asia/Kathmandu GMT+05:45 GMT+05:45

D/ID ( 4228): Asia/Katmandu GMT+05:45 GMT+05:45

D/ID ( 4228): Asia/Kolkata GMT+05:30 GMT+05:30

D/ID ( 4228): Asia/Krasnoyarsk GMT+07:00 GMT+07:00

D/ID ( 4228): Asia/Kuala_Lumpur GMT+08:00 GMT+08:00

D/ID ( 4228): Asia/Kuching GMT+08:00 GMT+08:00

D/ID ( 4228): Asia/Kuwait GMT+03:00 GMT+03:00

D/ID ( 4228): Asia/Macao 中国標準時 GMT+08:00

D/ID ( 4228): Asia/Macau 中国標準時 GMT+08:00

D/ID ( 4228): Asia/Magadan GMT+11:00 GMT+11:00

D/ID ( 4228): Asia/Makassar GMT+08:00 GMT+08:00

D/ID ( 4228): Asia/Manila GMT+08:00 GMT+08:00

D/ID ( 4228): Asia/Muscat GMT+04:00 GMT+04:00

D/ID ( 4228): Asia/Nicosia 東欧標準時 GMT+02:00

D/ID ( 4228): Asia/Novokuznetsk GMT+07:00 GMT+07:00

D/ID ( 4228): Asia/Novosibirsk GMT+06:00 GMT+06:00

D/ID ( 4228): Asia/Omsk GMT+06:00 GMT+06:00

D/ID ( 4228): Asia/Oral GMT+05:00 GMT+05:00

D/ID ( 4228): Asia/Phnom_Penh GMT+07:00 GMT+07:00

D/ID ( 4228): Asia/Pontianak GMT+07:00 GMT+07:00

D/ID ( 4228): Asia/Pyongyang GMT+09:00 GMT+09:00

D/ID ( 4228): Asia/Qatar GMT+03:00 GMT+03:00

D/ID ( 4228): Asia/Qyzylorda GMT+06:00 GMT+06:00

D/ID ( 4228): Asia/Rangoon GMT+06:30 GMT+06:30

D/ID ( 4228): Asia/Riyadh GMT+03:00 GMT+03:00

D/ID ( 4228): Asia/Saigon GMT+07:00 GMT+07:00

D/ID ( 4228): Asia/Sakhalin GMT+10:00 GMT+10:00

D/ID ( 4228): Asia/Samarkand GMT+05:00 GMT+05:00

D/ID ( 4228): Asia/Seoul GMT+09:00 GMT+09:00

D/ID ( 4228): Asia/Shanghai 中国標準時 GMT+08:00

D/ID ( 4228): Asia/Singapore GMT+08:00 GMT+08:00

D/ID ( 4228): Asia/Taipei GMT+08:00 GMT+08:00

D/ID ( 4228): Asia/Tashkent GMT+05:00 GMT+05:00

D/ID ( 4228): Asia/Tbilisi GMT+04:00 GMT+04:00

D/ID ( 4228): Asia/Tehran GMT+03:30 GMT+03:30

D/ID ( 4228): Asia/Tel_Aviv イスラエル標準時 GMT+02:00

D/ID ( 4228): Asia/Thimbu GMT+06:00 GMT+06:00

D/ID ( 4228): Asia/Thimphu GMT+06:00 GMT+06:00

D/ID ( 4228): Asia/Tokyo 日本標準時 JST

D/ID ( 4228): Asia/Ujung_Pandang GMT+08:00 GMT+08:00

D/ID ( 4228): Asia/Ulaanbaatar GMT+08:00 GMT+08:00

D/ID ( 4228): Asia/Ulan_Bator GMT+08:00 GMT+08:00

D/ID ( 4228): Asia/Urumqi 中国標準時 GMT+08:00

D/ID ( 4228): Asia/Vientiane GMT+07:00 GMT+07:00

D/ID ( 4228): Asia/Vladivostok GMT+10:00 GMT+10:00

D/ID ( 4228): Asia/Yakutsk GMT+09:00 GMT+09:00

D/ID ( 4228): Asia/Yekaterinburg GMT+05:00 GMT+05:00

D/ID ( 4228): Asia/Yerevan GMT+04:00 GMT+04:00

D/ID ( 4228): Atlantic/Azores GMT-01:00 GMT-01:00

D/ID ( 4228): Atlantic/Bermuda 大西洋標準時 GMT-04:00

D/ID ( 4228): Atlantic/Canary GMT+00:00 GMT+00:00

D/ID ( 4228): Atlantic/Cape_Verde GMT-01:00 GMT-01:00

D/ID ( 4228): Atlantic/Faeroe GMT+00:00 GMT+00:00

D/ID ( 4228): Atlantic/Faroe GMT+00:00 GMT+00:00

D/ID ( 4228): Atlantic/Jan_Mayen 中欧標準時 GMT+01:00

D/ID ( 4228): Atlantic/Madeira GMT+00:00 GMT+00:00

D/ID ( 4228): Atlantic/Reykjavik グリニッジ標準時 GMT+00:00

D/ID ( 4228): Atlantic/South_Georgia GMT-02:00 GMT-02:00

D/ID ( 4228): Atlantic/St_Helena グリニッジ標準時 GMT+00:00

D/ID ( 4228): Atlantic/Stanley GMT-04:00 GMT-04:00

D/ID ( 4228): Australia/ACT GMT+10:00 GMT+10:00

D/ID ( 4228): Australia/Adelaide GMT+09:30 GMT+09:30

D/ID ( 4228): Australia/Brisbane GMT+10:00 GMT+10:00

D/ID ( 4228): Australia/Broken_Hill GMT+09:30 GMT+09:30

D/ID ( 4228): Australia/Canberra GMT+10:00 GMT+10:00

D/ID ( 4228): Australia/Currie GMT+10:00 GMT+10:00

D/ID ( 4228): Australia/Darwin GMT+09:30 GMT+09:30

D/ID ( 4228): Australia/Eucla GMT+08:45 GMT+08:45

D/ID ( 4228): Australia/Hobart GMT+10:00 GMT+10:00

D/ID ( 4228): Australia/LHI GMT+10:30 GMT+10:30

D/ID ( 4228): Australia/Lindeman GMT+10:00 GMT+10:00

D/ID ( 4228): Australia/Lord_Howe GMT+10:30 GMT+10:30

D/ID ( 4228): Australia/Melbourne GMT+10:00 GMT+10:00

D/ID ( 4228): Australia/NSW GMT+10:00 GMT+10:00

D/ID ( 4228): Australia/North GMT+09:30 GMT+09:30

D/ID ( 4228): Australia/Perth GMT+08:00 GMT+08:00

D/ID ( 4228): Australia/Queensland GMT+10:00 GMT+10:00

D/ID ( 4228): Australia/South GMT+09:30 GMT+09:30

D/ID ( 4228): Australia/Sydney GMT+10:00 GMT+10:00

D/ID ( 4228): Australia/Tasmania GMT+10:00 GMT+10:00

D/ID ( 4228): Australia/Victoria GMT+10:00 GMT+10:00

D/ID ( 4228): Australia/West GMT+08:00 GMT+08:00

D/ID ( 4228): Australia/Yancowinna GMT+09:30 GMT+09:30

D/ID ( 4228): Brazil/Acre GMT-05:00 GMT-05:00

D/ID ( 4228): Brazil/DeNoronha GMT-02:00 GMT-02:00

D/ID ( 4228): Brazil/East ブラジリア時間 GMT-03:00

D/ID ( 4228): Brazil/West アマゾン時間 GMT-04:00

D/ID ( 4228): CET GMT+01:00 GMT+01:00

D/ID ( 4228): CST6CDT アメリカ中部標準時 GMT-06:00

D/ID ( 4228): Canada/Atlantic 大西洋標準時 GMT-04:00

D/ID ( 4228): Canada/Central アメリカ中部標準時 GMT-06:00

D/ID ( 4228): Canada/East-Saskatchewan アメリカ中部標準時 GMT-06:00

D/ID ( 4228): Canada/Eastern アメリカ東部標準時 GMT-05:00

D/ID ( 4228): Canada/Mountain アメリカ山地標準時 GMT-07:00

D/ID ( 4228): Canada/Newfoundland ニューファンドランド島標準時 GMT-03:30

D/ID ( 4228): Canada/Pacific アメリカ太平洋標準時 GMT-08:00

D/ID ( 4228): Canada/Saskatchewan アメリカ中部標準時 GMT-06:00

D/ID ( 4228): Canada/Yukon アメリカ太平洋標準時 GMT-08:00

D/ID ( 4228): Chile/Continental GMT-04:00 GMT-04:00

D/ID ( 4228): Chile/EasterIsland GMT-06:00 GMT-06:00

D/ID ( 4228): Cuba GMT-05:00 GMT-05:00

D/ID ( 4228): EET GMT+02:00 GMT+02:00

D/ID ( 4228): EST GMT-05:00 GMT-05:00

D/ID ( 4228): EST5EDT アメリカ東部標準時 GMT-05:00

D/ID ( 4228): Egypt 東欧標準時 GMT+02:00

D/ID ( 4228): Eire グリニッジ標準時 GMT+00:00

D/ID ( 4228): Etc/GMT GMT+00:00 GMT+00:00

D/ID ( 4228): Etc/GMT+0 GMT+00:00 GMT+00:00

D/ID ( 4228): Etc/GMT+1 GMT-01:00 GMT-01:00

D/ID ( 4228): Etc/GMT+10 GMT-10:00 GMT-10:00

D/ID ( 4228): Etc/GMT+11 GMT-11:00 GMT-11:00

D/ID ( 4228): Etc/GMT+12 GMT-12:00 GMT-12:00

D/ID ( 4228): Etc/GMT+2 GMT-02:00 GMT-02:00

D/ID ( 4228): Etc/GMT+3 GMT-03:00 GMT-03:00

D/ID ( 4228): Etc/GMT+4 GMT-04:00 GMT-04:00

D/ID ( 4228): Etc/GMT+5 GMT-05:00 GMT-05:00

D/ID ( 4228): Etc/GMT+6 GMT-06:00 GMT-06:00

D/ID ( 4228): Etc/GMT+7 GMT-07:00 GMT-07:00

D/ID ( 4228): Etc/GMT+8 GMT-08:00 GMT-08:00

D/ID ( 4228): Etc/GMT+9 GMT-09:00 GMT-09:00

D/ID ( 4228): Etc/GMT-0 GMT+00:00 GMT+00:00

D/ID ( 4228): Etc/GMT-1 GMT+01:00 GMT+01:00

D/ID ( 4228): Etc/GMT-10 GMT+10:00 GMT+10:00

D/ID ( 4228): Etc/GMT-11 GMT+11:00 GMT+11:00

D/ID ( 4228): Etc/GMT-12 GMT+12:00 GMT+12:00

D/ID ( 4228): Etc/GMT-13 GMT+13:00 GMT+13:00

D/ID ( 4228): Etc/GMT-14 GMT+14:00 GMT+14:00

D/ID ( 4228): Etc/GMT-2 GMT+02:00 GMT+02:00

D/ID ( 4228): Etc/GMT-3 GMT+03:00 GMT+03:00

D/ID ( 4228): Etc/GMT-4 GMT+04:00 GMT+04:00

D/ID ( 4228): Etc/GMT-5 GMT+05:00 GMT+05:00

D/ID ( 4228): Etc/GMT-6 GMT+06:00 GMT+06:00

D/ID ( 4228): Etc/GMT-7 GMT+07:00 GMT+07:00

D/ID ( 4228): Etc/GMT-8 GMT+08:00 GMT+08:00

D/ID ( 4228): Etc/GMT-9 GMT+09:00 GMT+09:00

D/ID ( 4228): Etc/GMT0 GMT+00:00 GMT+00:00

D/ID ( 4228): Etc/Greenwich GMT+00:00 GMT+00:00

D/ID ( 4228): Etc/UCT GMT+00:00 GMT+00:00

D/ID ( 4228): Etc/UTC GMT+00:00 GMT+00:00

D/ID ( 4228): Etc/Universal GMT+00:00 GMT+00:00

D/ID ( 4228): Etc/Zulu GMT+00:00 GMT+00:00

D/ID ( 4228): Europe/Amsterdam 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Andorra 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Athens 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Belfast グリニッジ標準時 GMT+00:00

D/ID ( 4228): Europe/Belgrade 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Berlin 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Bratislava 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Brussels 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Bucharest 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Budapest 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Chisinau 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Copenhagen 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Dublin グリニッジ標準時 GMT+00:00

D/ID ( 4228): Europe/Gibraltar 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Guernsey グリニッジ標準時 GMT+00:00

D/ID ( 4228): Europe/Helsinki 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Isle_of_Man グリニッジ標準時 GMT+00:00

D/ID ( 4228): Europe/Istanbul 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Jersey グリニッジ標準時 GMT+00:00

D/ID ( 4228): Europe/Kaliningrad 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Kiev 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Lisbon GMT+00:00 GMT+00:00

D/ID ( 4228): Europe/Ljubljana 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/London グリニッジ標準時 GMT+00:00

D/ID ( 4228): Europe/Luxembourg 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Madrid 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Malta 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Mariehamn 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Minsk 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Monaco 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Moscow GMT+03:00 GMT+03:00

D/ID ( 4228): Europe/Nicosia 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Oslo 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Paris 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Podgorica 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Prague 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Riga 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Rome 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Samara GMT+04:00 GMT+04:00

D/ID ( 4228): Europe/San_Marino 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Sarajevo 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Simferopol 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Skopje 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Sofia 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Stockholm 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Tallinn 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Tirane 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Tiraspol 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Uzhgorod 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Vaduz 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Vatican 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Vienna 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Vilnius 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Volgograd GMT+03:00 GMT+03:00

D/ID ( 4228): Europe/Warsaw 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Zagreb 中欧標準時 GMT+01:00

D/ID ( 4228): Europe/Zaporozhye 東欧標準時 GMT+02:00

D/ID ( 4228): Europe/Zurich 中欧標準時 GMT+01:00

D/ID ( 4228): Factory GMT+00:00 GMT+00:00

D/ID ( 4228): GB グリニッジ標準時 GMT+00:00

D/ID ( 4228): GB-Eire グリニッジ標準時 GMT+00:00

D/ID ( 4228): GMT GMT+00:00 GMT+00:00

D/ID ( 4228): GMT+0 GMT+00:00 GMT+00:00

D/ID ( 4228): GMT-0 GMT+00:00 GMT+00:00

D/ID ( 4228): GMT0 GMT+00:00 GMT+00:00

D/ID ( 4228): Greenwich GMT+00:00 GMT+00:00

D/ID ( 4228): HST GMT-10:00 GMT-10:00

D/ID ( 4228): Hongkong GMT+08:00 GMT+08:00

D/ID ( 4228): Iceland グリニッジ標準時 GMT+00:00

D/ID ( 4228): Indian/Antananarivo 東アフリカ時間 GMT+03:00

D/ID ( 4228): Indian/Chagos GMT+06:00 GMT+06:00

D/ID ( 4228): Indian/Christmas GMT+07:00 GMT+07:00

D/ID ( 4228): Indian/Cocos GMT+06:30 GMT+06:30

D/ID ( 4228): Indian/Comoro 東アフリカ時間 GMT+03:00

D/ID ( 4228): Indian/Kerguelen GMT+05:00 GMT+05:00

D/ID ( 4228): Indian/Mahe GMT+04:00 GMT+04:00

D/ID ( 4228): Indian/Maldives GMT+05:00 GMT+05:00

D/ID ( 4228): Indian/Mauritius GMT+04:00 GMT+04:00

D/ID ( 4228): Indian/Mayotte 東アフリカ時間 GMT+03:00

D/ID ( 4228): Indian/Reunion GMT+04:00 GMT+04:00

D/ID ( 4228): Iran GMT+03:30 GMT+03:30

D/ID ( 4228): Israel イスラエル標準時 GMT+02:00

D/ID ( 4228): Jamaica アメリカ東部標準時 GMT-05:00

D/ID ( 4228): Japan 日本標準時 JST

D/ID ( 4228): Kwajalein GMT+12:00 GMT+12:00

D/ID ( 4228): Libya 東欧標準時 GMT+02:00

D/ID ( 4228): MET GMT+01:00 GMT+01:00

D/ID ( 4228): MST GMT-07:00 GMT-07:00

D/ID ( 4228): MST7MDT アメリカ山地標準時 GMT-07:00

D/ID ( 4228): Mexico/BajaNorte アメリカ太平洋標準時 GMT-08:00

D/ID ( 4228): Mexico/BajaSur アメリカ山地標準時 GMT-07:00

D/ID ( 4228): Mexico/General アメリカ中部標準時 GMT-06:00

D/ID ( 4228): NZ GMT+12:00 GMT+12:00

D/ID ( 4228): NZ-CHAT GMT+12:45 GMT+12:45

D/ID ( 4228): Navajo アメリカ山地標準時 GMT-07:00

D/ID ( 4228): PRC 中国標準時 GMT+08:00

D/ID ( 4228): PST8PDT アメリカ太平洋標準時 GMT-08:00

D/ID ( 4228): Pacific/Apia GMT-11:00 GMT-11:00

2011年6月2日木曜日

メルマガはじめます。

ちょっと、手続きに失敗してて、
なんと明日第1回発行なのに
今告知とか、、、、orz

毎週金曜日の夜に発行します〜。

このブログでは、特定のチップスについて取り上げてますが、
このメルマガでは、あるアプリを取り上げて、
その UI のポイントとか実装方法とかを紹介します。

今月は「Google IO 2011 の公式アプリ」の
中身を数回に分けて解説します。

よかったら購読してください!
↓でサンプルも見れます。

あんざいゆきのAndroidアプリUI研究部!
---
毎週、カッコいい Android アプリを取り上げて、
使いやすさやデザインについて研究します!
プログラミング上でのポイントや同じような UI を
実現するための方法についても紹介しちゃいます。

powered by まぐまぐ
---

Android null もしくは 0 length を判定する

文字列が null か 0-length の場合と、それ以外の場合で処理を分けたい場合
null 判定と String.length の判定を使って


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

Log.d("[checkLength] null", isNullOrZeroLength(null) + "");
Log.d("[checkLength] length 0", isNullOrZeroLength("") + "");
Log.d("[checkLength] length 5", isNullOrZeroLength("Hello") + "");
}

public boolean isNullOrZeroLength(String s) {
if(s == null) {
return true;
}
else if(s.length() == 0){
return true;
}
else {
return false;
}
}


とやればできますが、android.text.TextUtils.isEmpty(CharSequence str) を使えば null と 0-length 両方を判定してくれます。
このメソッドは引数の str が null もしくは 0-length の場合に true を返します。
このメソッドを使って書き換えると


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

Log.d("[TextUtils] null", isNullOrZeroLength(null) + "");
Log.d("[TextUtils] length 0", isNullOrZeroLength("") + "");
Log.d("[TextUtils] length 5", isNullOrZeroLength("Hello") + "");
}

public boolean isNullOrZeroLength(String s) {
return TextUtils.isEmpty(s);
}


それぞれの出力

D/[checkLength] null(22715): true
D/[checkLength] length 0(22715): true
D/[checkLength] length 5(22715): false
D/[TextUtils] null(22715): true
D/[TextUtils] length 0(22715): true
D/[TextUtils] length 5(22715): false