こんにちはKID.Aです。
今回はLiveCardを使って、timelineに表示するアプリを作ってみます。
まず、実装の前にGoogle Glassの基礎動作やUIについて説明します。
基本動作
Google Glassを起動すると下記のホームが表示されます。
右スワイプで過去の情報、左スワイプで現在/未来の情報や設定画面に遷移できます。
これらの軸をtimelineと呼びます。
UI要素について
・Static card
テキスト、HTML、画像、ビデオなどを表示する。live cardsかimmersionsを実装できます。
ホームからタップを押下して、static cardが表示されます。
・Live card
高頻度でレンダリングしたり、現時点で重要だったりするカードを表示します。
例えば、ストップウォチやカレンダーなどを作成する際に利用します。
動作は、上記のようにServiceを経由してLive cardを追加します。
追加したLive cardはtimelineに残ります。また、下スワイプで終了させてもtimelineからは消えず、アプリは終了しません。Live cardを消したい場合はService経由で削除することができます。
・Immersion
timelineの上で体験できるAndroidのActivityを表示します。
「Google Glassの簡単なアプリを作る」で作成したアプリもこちらになります。
下スワイプでアプリを終了させることができます。
参考URL
https://developers.google.com/glass/design/patterns
実装
それではLive cardを実装してみます。以下の手順を記載します。
- /res/layout/activity_main.xmlを作成
- /res/values/strings.xmlに下記を追加します
- /res/xml/voice_trigger.xmlを作成します
- /res/menu/main.xmlを作成します
- MainAcitvity.javaを作成します
- MainSerivceを作ります
- AndroidManifest.xmlの設定を記載します
これはLiveCardで表示するレイアウトになります。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/textView1" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:gravity="center" android:text="" android:textSize="48dp" /> </RelativeLayout>
<string name="voice_trigger">LiveSample</string> <string name="action_finish">finish</string>
<?xml version="1.0" encoding="utf-8"?> <trigger keyword="@string/voice_trigger" />
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:context="com.example.glasslivecard.MainActivity" > <item android:id="@+id/action_finish" android:orderInCategory="100" android:showAsAction="never" android:title="@string/action_finish"/> </menu>
これはLiveCardからタッチした際に表示するActivityです。
中身はタッチするとServiceを終了する処理になっています。
public class MainActivity extends Activity { @Override public void onAttachedToWindow() { super.onAttachedToWindow(); openOptionsMenu(); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_finish: stopService(new Intent(this, MainService.class)); return true; default: return super.onOptionsItemSelected(item); } } @Override public void onOptionsMenuClosed(Menu menu) { finish(); } }
こちらが今回のメインで、LiveCardを挿入するサービスになります。
簡単にソースを説明すると、RemoteViews(別プロセス上のレイアウトを操作する場合に必要なView)を作成して、LiveCard#setViewsでレイアウトを設定しています。また、LiveCard#setActionでタッチパッドのタッチするとMainActivityが起動するように設定しています。
LiveCard#publishは、actionとremote views(もしくはdirect rendering)を設定していれば、time lineにLive cardを表示できます。
Serviceが終了するonDestoryの中で、LiveCardが表示されている場合にLiveCard#unpublishを使用して、time lineから非表示にしています。
public class MainService extends Service { @Override public IBinder onBind(Intent intent) { return null; } private static final String LIVE_CARD_TAG = "livecardSample"; private LiveCard mLiveCard; private RemoteViews remoteView; private final Handler mHandler = new Handler(); private final UpdateLiveCardRunnable mUpdateLiveCardRunnable = new UpdateLiveCardRunnable(); private static final long DELAY_MILLIS = 10 * 1000; private int count = 0; @Override public int onStartCommand(Intent intent, int flags, int startId) { if (mLiveCard == null) { mLiveCard = new LiveCard(this, LIVE_CARD_TAG); remoteView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.activity_main); remoteView.setTextViewText(R.id.textView1, "" + count); mLiveCard.setViews(remoteView); Intent menuIntent = new Intent(this, MainActivity.class); menuIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); mLiveCard.setAction(PendingIntent.getActivity(this, 0, menuIntent, 0)); mLiveCard.publish(PublishMode.REVEAL); mHandler.postDelayed(mUpdateLiveCardRunnable, DELAY_MILLIS); } else { mLiveCard.navigate(); } return START_STICKY; } private class UpdateLiveCardRunnable implements Runnable{ private boolean mIsStopped = false; public void run(){ if(!isStopped()){ count++; remoteView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.activity_main); remoteView.setTextViewText(R.id.textView1, "" + count); mLiveCard.setViews(remoteView); mHandler.postDelayed(mUpdateLiveCardRunnable, DELAY_MILLIS); } } public boolean isStopped() { return mIsStopped; } public void setStop(boolean isStopped) { this.mIsStopped = isStopped; } } @Override public void onDestroy() { if (mLiveCard != null && mLiveCard.isPublished()) { mUpdateLiveCardRunnable.setStop(true); mLiveCard.unpublish(); mLiveCard = null; } super.onDestroy(); } }
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.glasslivecard" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" /> <uses-permission android:name="com.google.android.glass.permission.DEVELOPMENT" /> <application> <service android:name="com.example.glasslivecard.MainService" android:exported="true" android:label="@string/app_name" > <intent-filter> <action android:name="com.google.android.glass.action.VOICE_TRIGGER" /> </intent-filter> <meta-data android:name="com.google.android.glass.VoiceTrigger" android:resource="@xml/voice_trigger" /> </service> <activity android:name="com.example.glasslivecard.MainActivity" /> </application> </manifest>
実際に動かしてみます
- 作成したアプリをインストールします
- アプリを起動します。(Livesampleをタッチします)
- Live cardがtimelineに追加されます
- 10秒毎に数字がカウントアップしていきます
- タッチパッドをタッチすると、MainActivityが起動します
- タッチするとServiceが終了して、timelineからLive cardが消えます
起動後、以下のようにLive cardがtimelineに追加されます。
LiveCardの実装と挙動について参考になれば幸いです。