android wear development
TRANSCRIPT
https://fb.com/j796160836
Johnny SungMobile devices Developer
https://plus.google.com/+JohnnySung
http://about.me/j796160836
Friday, January 30, 15
Agenda• Overview
• Basic Setup
• Notifications
• Layout
• Data Layer
• Comparison with WatchKit
Friday, January 30, 15
http://www.droid-life.com/wp-content/uploads/2014/03/android-wear.png
https://www.youtube.com/watch?v=CZrGDo9Grgk (30sec)https://www.youtube.com/watch?v=QrqZl2QIz0c (1.5min)
TVCF
Friday, January 30, 15
Android Wear 設計理念
• Launched automatically (⾃自動啟動)
• Glanceable (可⼀一眼瞥⾒見)
• All about suggest and demand (推薦與需求)
• Zero or low interaction (盡可能減少點擊滑動步驟)
Friday, January 30, 15
http://core0.staticworld.net/images/article/2014/06/android_wear_sports_score-100314393-orig.jpg
Friday, January 30, 15
Debugging over Bluetooth
• ⼿手機設定 Debugging over bluetooth
• ⼿手錶設定 ADB Debug & Debugging over bluetooth
• 執⾏行 ConnectDebugWear.shhttps://gist.github.com/j796160836/9b135a8de4c44846fd82
Friday, January 30, 15
Android Wear Emulator & Phone Emulator
• Choose x86 Emulator (faster)
• Install Google Search (2min)com.google.android.googlequicksearchbox-3.6.16.1614640.x86.apk
• Install Android Wear (2min)com.google.android.wearable.app
• Be patient
Friday, January 30, 15
WearHost for Genymotion• Install ARM Translation Installer
Genymotion-ARM-Translation_v1.1.zip
• Install GAppsgapps-lp-20141109-signed.zipgapps-kk-20140606-signed.zipgapps-jb-20130813-signed.zip
• Install Google Searchcom.google.android.googlequicksearchbox
• Install Android Wearcom.google.android.wearable.app
• Run Script: ConnectWearEmulator.shhttps://gist.github.com/j796160836/91e77ca819c11ed7bf01
Friday, January 30, 15
Compile Wear in Eclipse• Install Google Repository in SDK Manager
• Find wearable-1.1.0.aar in SDKandroid-sdks/extras/google/m2repository/com/google/android/support/wearable/1.0.0/wearable-1.1.0.aar
• Rename aar to zip and unzip it
• Import from existing code
• Check it Is Library
• Import google-play-service-libhttps://medium.com/@tangtungai/ef1b34126a5d
Friday, January 30, 15
Compile Wear in Android Studio
• Edit Gradle
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.google.android.support:wearable:1.1.0' compile 'com.google.android.gms:play-services-wearable:6.5.87'}
[Wearable module]
Friday, January 30, 15
Packaging structure
Android App
Android App
Code
Resources
Code
ResourcesWear App
WearApp Module HandledApp Module
Friday, January 30, 15
Packaging Wearables using Ant
1. Export and Sign Wearable Apps APK(eg: demowearapp.apk)
2. Add a meta-data tag in AndroidManifest.xml
3. Put your wearable binary in res/raw directory(eg: res/raw/demowearapp.apk)
4. Write reference descriptions xml(eg: res/xml/wearable_app_desc.xml)
5. Turn off Asset Compressionhttps://developer.android.com/training/wearables/apps/packaging.html#PackageManuallyhttps://medium.com/@tangtungai/ef1b34126a5d
Friday, January 30, 15
Packaging Wearables in Eclipse
1. Export and Sign Wearable Apps APK(eg: demowearapp.apk)
#!/bin/bash
cd ../wearable-1.1.0android update lib-project --path .ant clean release
cd ../DemoWearAppandroid update project --path .ant clean release
key.store=TEST.keystorekey.store.password=PASSWORDkey.alias=KEYSTORE_ALIASkey.alias.password=PASSWORDconfig.logging=true
ant.properties
Generate build.xml
[Wearable project]
Friday, January 30, 15
Packaging Wearables in Eclipse
2. Add a meta-data tag in AndroidManifest.xml
<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <!-- ... -->
<meta-data android:name="com.google.android.wearable.beta.app" android:resource="@xml/wearable_app_desc" /></application>
[Smartphone project]
Friday, January 30, 15
Packaging Wearables in Eclipse
3. Put your wearable binary in res/raw directory(eg: res/raw/demowearapp.apk)
4. Write reference descriptions xml(eg: res/xml/wearable_app_desc.xml)
<?xml version="1.0" encoding="utf-8"?><wearableApp package="com.example.demowearapp"> <versionCode>1</versionCode> <versionName>1.0</versionName> <rawPathResId>demowearapp</rawPathResId> </wearableApp>
[Smartphone project]
Friday, January 30, 15
Packaging Wearables in Eclipse5. Turn off Asset Compression
<target name="-package-resources" depends="-crunch"> <do-only-if-not-library elseText="Library project: do not package resources..." > <aapt executable="${aapt}" command="package" versioncode="${version.code}" versionname="${version.name}" debug="${build.is.packaging.debug}" manifest="${out.manifest.abs.file}" assets="${asset.absolute.dir}" androidjar="${project.target.android.jar}" apkfolder="${out.absolute.dir}" nocrunch="${build.packaging.nocrunch}" resourcefilename="${resource.package.file.name}" resourcefilter="${aapt.resource.filter}" libraryResFolderPathRefid="project.library.res.folder.path" libraryPackagesRefid="project.library.packages" libraryRFileRefid="project.library.bin.r.file.path" previousBuildType="${build.last.target}" buildType="${build.target}" ignoreAssets="${aapt.ignore.assets}"> <res path="${out.res.absolute.dir}" /> <res path="${resource.absolute.dir}" />
<nocompress extension="apk" /> </aapt> </do-only-if-not-library></target>
http://stackoverflow.com/questions/7937368/how-to-pass-arguments-to-aapt-when-building-android-apk
From <sdks_dir>/tools/ant/build.xml, search -package-resources
Add this
[Smartphone project]
Friday, January 30, 15
[Smartphone module]dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:21.0.3' compile 'com.google.android.gms:play-services:6.5.87' wearApp project(':wear')}
Packaging Wearables in Android Studio
• Edit Gradle
Friday, January 30, 15
Code Examples
AndroidWearable-Sampleshttp://goo.gl/q8qfm8
DemoWearApphttp://goo.gl/pIJzr4
Friday, January 30, 15
Notification 整理• Notification 到底要怎麼發?
• 從⼿手機發送
• 同步到⼿手錶(含變更樣式)
• 不同步到⼿手錶
• 從⼿手錶發送
mBuilder.setLocalOnly(true);
Friday, January 30, 15
Notification 整理• Notification 變更樣式?
• 從⼿手機發送
• 設定⼿手機樣式,再變更⼿手錶樣式
• ⾃自訂樣式(只適⽤用於⼿手機)
• 從⼿手錶發送
• ⾃自訂樣式(只適⽤用於⼿手錶)
WearableExtender
WearableExtendersetDisplayIntent
RemoteView
Friday, January 30, 15
基本款 Notification
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);mBuilder.setSmallIcon(R.drawable.ic_launcher);mBuilder.setContentTitle("Awesome app");mBuilder.setContentText("The description");Notification notification = mBuilder.build();
int NOTIFICATION_ID = 0x01;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);mNotificationManager.notify(NOTIFICATION_ID, notification);
建⽴立
發送
Friday, January 30, 15
Notification notification = new Notification(R.drawable.ic_launcher, "New Notification!", System.currentTimeMillis());notification.setLatestEventInfo(this , "Awesome app" , "The description", pending);
基本款 Notification
舊寫法 (deprecated in API level 11)
拜託別再寫了哈~Friday, January 30, 15
基本款 Notification
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);mBuilder.setSmallIcon(R.drawable.ic_launcher);mBuilder.setContentTitle("Awesome app");mBuilder.setContentText("The description");Notification notification = mBuilder.build();
Friday, January 30, 15
Android 4.1 (API 16)BigTextStyle
String msg = "\"The quick brown fox …";NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);mBuilder.setSmallIcon(R.drawable.ic_launcher);mBuilder.setContentTitle("Awesome app");mBuilder.setContentText(msg);mBuilder.setStyle( new NotificationCompat.BigTextStyle() .bigText(msg));Notification notification = mBuilder.build();
http://developer.android.com/training/notify-user/expanded.htmlFriday, January 30, 15
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);mBuilder.setSmallIcon(R.drawable.photo);mBuilder.setContentTitle("Awesome app");mBuilder.setContentText("The description");
Bitmap bg = BitmapFactory.decodeResource( getResources(), R.drawable.bg);
NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle();style.bigPicture(bg);style.setBigContentTitle("My title");style.setSummaryText("The description");mBuilder.setStyle(style);
Notification notification = mBuilder.build();
BigPictureStyle Android 4.1 (API 16)
Friday, January 30, 15
Notification Height Limit• Normal view layouts: 64 dp
• Expanded view layouts: 256 dp.
256dp
64dp
Friday, January 30, 15
基本款 Notification + 背景
• Background size• 400 x 400• 640 x 400 (Parallax scrolling)
• Put the picture at /res/drawable-nodpi
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);mBuilder.setSmallIcon(R.drawable.photo);mBuilder.setContentTitle("Awesome app");mBuilder.setContentText("The description");
Bitmap bg = BitmapFactory.decodeResource( getResources(), R.drawable.bg);
NotificationCompat.WearableExtender wearExt = new NotificationCompat.WearableExtender() .setBackground(bg);mBuilder.extend(wearExt);
Notification notification = mBuilder.build();
Friday, January 30, 15
RemoteControlClient Android 4.0 (API 14)
• Attribute Keys• METADATA_KEY_ARTIST• METADATA_KEY_TITLE
• Attribute Keys• METADATA_KEY_TITLE• METADATA_KEY_ALBUM
RemoteView
Friday, January 30, 15
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);mBuilder.setSmallIcon(R.drawable.photo);mBuilder.setContentTitle("Awesome app");mBuilder.setContentText("The description");
Intent clickInt = new Intent(MainActivity.this, SecondActivity.class);
PendingIntent clickPenInt = PendingIntent.getActivity(this, 0, clickInt, PendingIntent.FLAG_UPDATE_CURRENT);mBuilder.setContentIntent(clickPenInt);
Notification notification = mBuilder.build();
按鈕按下的⾏行為
Friday, January 30, 15
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);mBuilder.setSmallIcon(R.drawable.ic_launcher);mBuilder.setContentTitle("世新廣播電臺");mBuilder.setContentText("⼤大⾃自然守護者...");
mBuilder.addAction(new NotificationCompat.Action (R.drawable.cancel, "Dismiss", dismissPenInt));mBuilder.addAction(new NotificationCompat.Action (R.drawable.listen, "Listen FM", listenPenInt));
NotificationCompat.WearableExtender wExt = new NotificationCompat.WearableExtender();
wExt.addAction(new NotificationCompat.Action (R.drawable.listen_wear, "Listen FM",listenPenInt));
Notification notification = mBuilder.build();
http://stackoverflow.com/questions/25026616/android-wear-action-item-icon-sizing-vs-phone-notification-action-item-sizing
The solutions
Friday, January 30, 15
Code ExamplesWearable Notificationshttps://github.com/googlesamples/android-Notifications
Basic Notificationshttps://github.com/googlesamples/android-BasicNotifications
Custom Notificationshttps://github.com/googlesamples/android-CustomNotifications
Friday, January 30, 15
• WatchViewStub
• 指定 Round layout
• 指定 Rect layout
• BoxInsetLayout
• 設定 app:layout_box="all"
Layout
http://developer.android.com/training/wearables/ui/layouts.htmlFriday, January 30, 15
Layout• Card
• 繼承 CardFragment
• CardScrollView + CardFrame
http://developer.android.com/training/wearables/ui/cards.htmlFriday, January 30, 15
Data Items
• Path唯⼀一的字符串,必須以正斜線開始例如:/path/to/data
• Payload⼀一個字節數組,你可允許進⾏行對象的序列化 (Serialize) 與反序列化 (Deserialize)⼤大⼩小不能超過100KB。
PathPayload
100KB
Friday, January 30, 15
連接⽅方式• 在 Activity 中建⽴立連線,實作 Callback 監聽
• 使⽤用 WearableListenerService
• Callback
• DataApi.DataListener
• MessageApi.MessageListener
• NodeApi.NodeListener
Friday, January 30, 15
Listener• DataApi.DataListener
• NodeApi.NodeListener
• MessageApi.MessageListener
public void onPeerConnected(Node node)public void onPeerDisconnected(Node node)
public void onDataChanged(DataEventBuffer dataEvents)
public void onMessageReceived(MessageEvent messageEvent)
Friday, January 30, 15
連接⽅方式• onCreate() 建⽴立 GoogleApiClient,掛載 Callbacks
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
mGoogleApiClient = new GoogleApiClient.Builder(this) .addApi(Wearable.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build();}
Friday, January 30, 15
連線 Callback
public void onConnected(Bundle connectionHint)
public void onConnectionFailed(ConnectionResult result)
• GoogleApiClient.ConnectionCallbacks
• GoogleApiClient.OnConnectionFailedListenerpublic void onConnectionSuspended(int cause)
https://developer.android.com/google/auth/api-client.html
Friday, January 30, 15
連接⽅方式• onStart() 呼叫 connect() 做連線@Overrideprotected void onStart() { super.onStart(); if (!mResolvingError) { mGoogleApiClient.connect(); }}
@Override //ConnectionCallbackspublic void onConnected(Bundle connectionHint) { Log.d(TAG, "Google API Client was connected"); mResolvingError = false; Wearable.DataApi.addListener(mGoogleApiClient, this); Wearable.MessageApi.addListener(mGoogleApiClient, this); Wearable.NodeApi.addListener(mGoogleApiClient, this);}
• onConnected() 時候,掛載 Listener
Friday, January 30, 15
@Overrideprotected void onStop() { if (!mResolvingError) { Wearable.DataApi.removeListener(mGoogleApiClient, this); Wearable.MessageApi.removeListener(mGoogleApiClient, this); Wearable.NodeApi.removeListener(mGoogleApiClient, this); mGoogleApiClient.disconnect(); } super.onStop();}
連接⽅方式• onStop() 移除Listener,並且斷線
Friday, January 30, 15
WearableListenerService• 在系統需要的時候就會⾃自動綁定
<service android:name=".DataLayerListenerService" > <intent-filter> <action android:name="com.google.android.gms.wearable.BIND_LISTENER" /> </intent-filter></service>
public class DataLayerListenerService extends WearableListenerService {
@Override public void onDataChanged(DataEventBuffer dataEvents) { // Do Somthing } @Override public void onMessageReceived(MessageEvent messageEvent) { // Do Somthing }}
AndroidManifest.xml
Friday, January 30, 15
DataItemprivate void sendSampleDataItem() { final PutDataMapRequest putRequest = PutDataMapRequest.create("/SAMPLE"); final DataMap map = putRequest.getDataMap(); map.putInt("num", 12345); map.putString("example", "Sample String"); Wearable.DataApi.putDataItem(mGoogleApiClient, putRequest.asPutDataRequest());}
@Overridepublic void onDataChanged(DataEventBuffer dataEvents) { final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
dataEvents.close(); for(DataEvent event : events) { String path = event.getDataItem().getUri().getPath(); if(path.equals("/SAMPLE")) { final DataMap map = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); // read your values from map: int num = map.getInt("num"); String str = map.getString(“example"); Log.v(TAG, " num = " + num + " str = " + str); } }}
• 傳送
• 接收
Friday, January 30, 15
String msg = "Sample String";NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi .getConnectedNodes(mGoogleApiClient).await();for (Node node : nodes.getNodes()) { SendMessageResult result = Wearable.MessageApi.sendMessage( mGoogleApiClient, node.getId(), "/SAMPLE", msg.getBytes()) .await(); if (result.getStatus().isSuccess()) { // Success } else { // Error }}
@Overridepublic void onMessageReceived(MessageEvent messageEvent) {
if (messageEvent.getPath().equals("/SAMPLE")) { final String message = new String(messageEvent.getData());
// Do Something }}
• 傳送Message (AsyncTask)
• 接收Message
Message
Friday, January 30, 15
Code Examples
WearMessageBringFronthttp://goo.gl/jdKkHL
WearDataLayerDemohttp://goo.gl/HL84YQ
DataLayerhttps://github.com/googlesamples/android-DataLayer
Friday, January 30, 15
Android Wear
WatchKit
Packaging structure
Android App
Android App
CodeResources
Code
ResourcesWear App
WearApp Module HandledApp ModuleFriday, January 30, 15
Android Wear
Google Play ServicesGoogle Play Services
Android Device
ControllerModel
View
Mini
Controller
View
Model
Controller
View
Model
App structure
Android Wear
WatchKit
iOS App
Friday, January 30, 15
震動<uses-permission android:name="android.permission.VIBRATE" />
Vibrator mVibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
myVibrator.vibrate(100);
AndroidManifest.xml
Friday, January 30, 15
震動
mVibrator.vibrate(new long[]{80, 150, 80, 150, 80, 150}, -1);
Repeat
Pattern
mVibrator.cancel();
震動 Pattern
取消
Friday, January 30, 15
Troubleshooting: ⼿手錶 Offline 解決⽅方式
• Android Wear (設定 > 應⽤用程式)
1. 停⽤用
2. 清除資料
3. 啟⽤用
• Google Play 服務 (設定 > 應⽤用程式)
1. 清除所有資料(管理空間)
• 硬體
1. 重置⼿手錶
2. ⼿手機重新啟動
http://melix.github.io/blog/2014/10/android-moto360.html
$ adb devicesList of devices attached
dcfbbafd! devicelocalhost:4444 offline
Friday, January 30, 15
Android Wear(設定 > 應⽤用程式)
Google Play 服務 (設定 > 應⽤用程式 > 管理空間)
重置 Moto360
Troubleshooting: ⼿手錶 Offline 解決⽅方式
Friday, January 30, 15