Wednesday, December 11, 2013

My First "Google Glass App"

I made what my friends consider a poor purchasing decision - and what I hope will pay itself off in the long run. I bought an explorer edition of Google Glass.

The purpose, of course, was to start developing for them. I'm a programmer at heart and programming is what I want to do. It took about a week before I finally got around to making my first app, and of course being an Android developer I wanted to play with the newly released "Glass Development Kit" or GDK - the API on top of Android specifically for Google Glass Apps - not the mirror api cards. So I took apart one of Google's samples, as I'd never worked with Services or RemoteViews before.

It says "Meow" (on the card) when you trigger it using the voice command "OK Glass, Meow."

To do this, I had to create a few things: MeowService - the service running in the background that controls the LiveCard, meow_card.xml - the layout file that simply says "meow", MenuActivity - the required intent when interacting with the live card (yes, required!), and voice_trigger_start - a trigger with the text to launch the app, and of course AndroidManifest.xml, but that's barely important.

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="me.navarr.app.meow"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="15" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_lap"
android:label="@string/app_name" >
<activity
android:name="me.navarr.app.meow.MenuActivity"
android:label="@string/app_name"
android:theme="@style/MenuTheme"
android:enabled="true" >
</activity>
<service
android:name="me.navarr.app.meow.MeowService"
android:icon="@drawable/ic_lap"
android:label="@string/app_name"
android:enabled="true"
android:exported="true">
<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_start" />
</service>
</application>
</manifest>
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.6.+'
}
}
apply plugin: 'android'
repositories {
mavenCentral()
}
android {
compileSdkVersion "Google Inc.:Glass Development Kit Sneak Peek:15"
buildToolsVersion "18.1.1"
defaultConfig {
minSdkVersion 15
targetSdkVersion 15
}
buildTypes {
release {
runProguard true
proguardFile getDefaultProguardFile('proguard-android-optimize.txt')
}
}
productFlavors {
defaultFlavor {
proguardFile 'proguard-rules.txt'
}
}
}
dependencies {
}
view raw build.gradle hosted with ❤ by GitHub
<?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="@color/black" >
<TextView
android:id="@+id/seconds_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_marginTop="-10px"
android:text="@string/meow"
android:textSize="140sp"
android:gravity="center" />
</FrameLayout>
view raw card_meow.xml hosted with ❤ by GitHub
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/stop"
android:title="@string/stop"
android:icon="@drawable/ic_stop" />
</menu>
view raw menu.xml hosted with ❤ by GitHub
package me.navarr.app.meow;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
/**
* Activity showing the options menu.
*/
public class MenuActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
openOptionsMenu();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection.
switch (item.getItemId()) {
case R.id.stop:
stopService(new Intent(this, MeowService.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onOptionsMenuClosed(Menu menu) {
// Nothing else to do, closing the Activity.
finish();
}
}
package me.navarr.app.meow;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import android.widget.RemoteViews;
import com.google.android.glass.timeline.LiveCard;
import com.google.android.glass.timeline.TimelineManager;
public class MeowService extends Service {
private static final String TAG = "MeowService";
private static final String LIVE_CARD_ID = "meow_card";
private TimelineManager mTimelineManager;
private LiveCard mLiveCard;
@Override
public void onCreate() {
super.onCreate();
/**
* TimelineManager is the object to, well, manage the Timeline.
* If you're unfamiliar with the terminology, the timeline is the list
* of cards that appear on Glass. The cards to the right of the homescreen
* are cards from the past (notifications, emails, texts). Cards to the left of the
* homescreen are cards happening right now or in the future (LiveCards and Google Now)
*/
mTimelineManager = TimelineManager.from(this);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
/**
* For obvious reasons, you will probably only ever want ONE version of your
* LiveCard running
*/
if (mLiveCard == null) {
Log.d(TAG, "Publishing LiveCard");
mLiveCard = mTimelineManager.getLiveCard(LIVE_CARD_ID);
/**
* LiveCard.setViews is how we give it the low-frequency layout. For higher
* frequency rendering (real-time rendering) you will need to use
* LiveCard.enableDirectRendering and attach your rendering to LiveCard.getSurfaceHolder
*/
mLiveCard.setViews(new RemoteViews(this.getPackageName(), R.layout.card_meow));
/**
* Setting the LiveCard as non-silent means that we will be taken to the Timeline as
* soon as the card is published, rather than it just appearing in the background.
*
* If you are not doing anything else in the foreground, you definitely want to do this.
*/
mLiveCard.setNonSilent(true);
/**
* An action intent IS REQUIRED. After all, the user should have a way to stop your app
* or interact with it if there are other actions. Here we set the action as a
* PendingIntent to call up the MenuActivity.
*/
Intent menuIntent = new Intent(this, MenuActivity.class);
mLiveCard.setAction(PendingIntent.getActivity(this, 0, menuIntent, 0));
/**
* This immediately publishes the live card, and with setNonSilent as true, we're
* immediately taken to it.
*/
mLiveCard.publish();
Log.d(TAG, "Done publishing LiveCard");
}
return START_STICKY;
}
@Override
public void onDestroy() {
if (mLiveCard != null && mLiveCard.isPublished()) {
// I think you can figure out what we're doing here.
Log.d(TAG, "Unpublishing LiveCard");
mLiveCard.unpublish();
mLiveCard = null;
}
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string name="app_name">Meow</string>
<string name="stop">Stop</string>
<string name="open">Meow</string>
<string name="meow">ニャン</string>
</resources>
view raw strings.xml hosted with ❤ by GitHub
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<trigger keyword="@string/open" />

10 comments:

  1. you shared a great post and your concept is very good and thanks for this ossm site, if you are interested then you may like: top car of world

    ReplyDelete
  2. You blog post is just completely quality and informative. Many new facts and information which I have not heard about before. Keep sharing more blog posts: Records daily

    ReplyDelete
  3. Today, Christmas is both a religious and cultural holiday, centered around the birth of Jesus and celebrated all over the world. Midwinter ...

    ReplyDelete
  4. Comments on a handsome boy's pic · Impressive picture. · You look strong and confident. · That's a perfect gentlemen pic. · Wow! Looking handsome ...

    ReplyDelete
  5. As very long as merchandise will need being manufactured and assembled, there will be plenty of position options in just enterprises that serve a basic business functionality. click for more: is basic industries a good career path

    ReplyDelete
  6. He experienced a struggle with Lorena for more than 14 several years and experienced many hijas. Could or not it's that divided from me you will find there's ton of adoration nonetheless. click to get info: alexander shunnarah net worth

    ReplyDelete
  7. Doki Literature Club can be a transient time period actively playing American freeware visual novel video game established by group Salvato from the yr 2017. click here: how long is doki doki literature club

    ReplyDelete
  8. She has proven up on Television on Big-name Phantom Stories and Superstar Around fiascoes. As indicated by crystal gazing, Michael’s income is monitored. He wedded Lisa Kendrick...michael cooke kendrick

    ReplyDelete