連載
» 2014年12月03日 18時00分 公開

Android Wear用アプリの花形、時計アプリ「Watch Face」の基本的な作り方Androidで動く携帯Javaアプリ作成入門(56)(3/3 ページ)

[緒方聡,株式会社イーフロー]
前のページへ 1|2|3       

Watch Faceアプリのソースコード

 今回のソースコードは100ステップ未満で、分かりやすくなっています。不要な部分は省略し、ポイントとなる部分を説明していきます。

package com.example.ogata.watchfacesample;
 
public class WatchFaceActivity extends Activity {
    private TextView mTextViewDay;
    private boolean mAmbientMode;
    private final IntentFilter mFilter = new IntentFilter();
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        // 【1】
        private final String AMBIENT_MODE = "ambient_mode";
        {
            // 【2】
            mFilter.addAction("com.google.android.clockwork.home.action.BACKGROUND_ACTION");
            mFilter.addAction(Intent.ACTION_TIME_TICK);
            mFilter.addAction(Intent.ACTION_TIME_CHANGED);
            mFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
            mFilter.addAction(Intent.ACTION_DATE_CHANGED);
        }
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            // 【3】
            if (Intent.ACTION_TIME_TICK.equals(action) ||
                    Intent.ACTION_TIME_CHANGED.equals(action) ||
                    Intent.ACTION_DATE_CHANGED.equals(action) ||
                    Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
                displayToday();
                return;
            }
 
            // 【4】
            if (intent.hasExtra(AMBIENT_MODE)) {
                mAmbientMode = intent.getBooleanExtra(AMBIENT_MODE, false);
                Log.d(TAG, AMBIENT_MODE + "=" + mAmbientMode);
                updateAmbientMode();
            }
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        registerReceiver(mReceiver, mFilter);
        setContentView(R.layout.activity_watch_face);
        final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
        // 【5】
        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override
            public void onLayoutInflated(WatchViewStub stub) {
                mTextViewDay = (TextView) stub.findViewById(R.id.textViewDay);
                displayToday();
                updateAmbientMode();
            }
        });
    }
 
    private void displayToday() {
        if (mTextViewDay != null) {
            // 【6】
            Calendar calendar = Calendar.getInstance(TimeZone.getDefault());
            DateFormat df;
            if (Locale.getDefault().equals(Locale.JAPAN)) {
                df = new SimpleDateFormat("'" + getEraNameAndYear(calendar) + "' MM'月'dd'日' (E)");
            } else {
                df = new SimpleDateFormat("EEE MMM dd");
            }
            mTextViewDay.setText(df.format(calendar.getTime()));
        }
    }
 
    private void updateAmbientMode() {
        if (mTextViewDay != null) {
            mTextViewDay.setVisibility(mAmbientMode ? View.INVISIBLE : View.VISIBLE);
        }
    }
 
    // 【7】
    private String getEraNameAndYear(Calendar calendar) {
        // 簡単のため平成以降しか計算しない
        return "平成" + (calendar.get(Calendar.YEAR) - 1988) + "年";
    }
}

 Watch Faceアプリでは、「Ambient Mode」というブロードキャストを利用して、Android Wearの活性状態を取得します。

 具体的にはAmbient Modeがtrueの場合はディスプレーが薄暗く表示される非活性状態、falseの場合はディスプレーが明るく表示される活性状態になったことが通知されます。このモード変更を取得するには、【2】の"com.google.android.clockwork.home.action.BACKGROUND_ACTION"というアクションのブロードキャストを取得するようにし、【1】の"ambient_mode"というExtraが含まれているかどうかをチェックします。

 サンプルアプリでは、活性状態では和暦の年月日と時刻を表示し、非活性状態では時刻のみを表示するように制御しています。

 Ambient Mode以外の状態変更を検知するために、【2】で以下のアクションを追加しています。

  • Intent.ACTION_TIME_TICK
  • Intent.ACTION_TIME_CHANGED
  • Intent.ACTION_TIMEZONE_CHANGED
  • Intent.ACTION_DATE_CHANGED

 スマートフォン側で日付、時刻、タイムゾーンが変更されると、これらのアクションで通知がされるため、【3】のように日付を更新しています。時刻表示に関してはTextClockを使用しているため、本サンプルアプリでは制御する必要がないため、処理を省略しています。

 Ambient Modeが変更された場合は、その値をフィールドに保存し、保存された値を参照して年月日の表示/非表示を切り替えるメソッドを【4】で呼び出しています。わざわざフィールドに保存してからメソッドを呼び出している理由は、【5】で四角画面と丸画面を一意に扱えるWatchViewStubが非同期でレイアウトを完成させ、その動作完了後に現在のAmbient Modeを参照する必要があるためです。

 日付部分は、スマートフォンの言語設定が日本語なら和暦を、それ以外なら西暦を英語表記で表示します。その判定をしているのが【6】です。ポイントはCalendarからインスタンスを取得する際にTimeZoneを渡すこと、Localeの判定はJAPANESEではなくJAPANと比較することです。

 和暦の元号を表示する処理は【7】に実装してあります。簡易なため「平成」にしか対応していません。

コラム「Oracle Java VMだと和暦は……」

 実はOracle Java VMでは、以下のコードで和暦の元号を表示させることができるようになっています。

public class Wareki {
    public static void main(String[] args) {
        Locale locale = new Locale("ja", "JP", "JP");
        Locale.setDefault(locale);
        Calendar calendar = Calendar.getInstance();
        DateFormat df1 = new SimpleDateFormat("GGGG yyyy/M/d", locale);
        DateFormat df2 = new SimpleDateFormat("G yyyy/M/d", locale);
        System.out.println(df1.format(calendar.getTime()));
        System.out.println(df2.format(calendar.getTime()));
    }
}

 Oracle Java VMでは、以下のように出力されます。

平成 26/11/14
H 26/11/14

 これがAndroidだと以下のように出力されます。

西暦 2014/11/14
西暦 2014/11/14

 Oracle Java VMではロケールが日本の場合に、内部的にjava.util.JapaneseImperialCalendarが利用されるのですが、Androidではjava.util.GregorianCalendarのまま、という違いがあるためです。

 和暦を正確に表示したい場合、ICU4Jやその他のオープンソース実装を利用することになりますが、いずれにせよ元号が変更された場合はライブラリの差し替えが必要になるので、外部から取得するようにするのが正解かもしれません。


Watch Faceアプリ作成は今後に期待

 現段階でWatch Faceアプリを作成するには、非公式のAPIを利用するしかありません。コミュニティによってより詳しい作成方法が解き明かされているものの、まだ方法が分かっていないものも存在します。

 その1つが「Mute icon」と呼ばれる左上のミュート状態、バッテリ状態を示すアイコンの表示位置の制御です。

 デフォルトでは左上にあるのですが、組み込みのWatch Faceアプリではこのアイコンの位置を制御してデザインを損なわないようにしてあります。

 アイコンを囲む枠の色や、「Ok Google」の表示位置も制御できるようです。現段階ではこれらの位置がデフォルトの左上(丸画面では上中央)から変更できない、変更する方法が分かっていないため、このアイコンの位置に重要な情報がかぶってしまわないようにデザインする必要があります。

 今後正式に公開されるAPIには、Mute iconの表示位置の制御方法も含まれてくると期待しています。

 次回は、前回、前々回と解説してきたボイスアクションアプリを完成させます。お楽しみに。

「Androidで動く携帯Javaアプリ作成入門」バックナンバー
前のページへ 1|2|3       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。