aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Walker <jewalker@google.com>2016-05-12 20:24:07 -0700
committerJeremy Walker <jewalker@google.com>2016-05-13 14:04:21 -0700
commit351c8be73ed3a39cc8c8e3e4470ca2ec3b8586c6 (patch)
tree86219db6806f116e788971de1134349079b5709c
parent66abb49bfb0be83c9369b47915f00cfc29b0f1b9 (diff)
downloadandroid-351c8be73ed3a39cc8c8e3e4470ca2ec3b8586c6.tar.gz
Adds simple complication sample from I/O code lab (w/o use of Oli's library)
for public release after code lab. Change-Id: I19ec97231800d362701b2f45bd0a0d7f39770875
-rw-r--r--wearable/wear/WatchFace/Wearable/src/main/AndroidManifest.xml65
-rw-r--r--wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/ComplicationSimpleConfigActivity.java191
-rw-r--r--wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/ComplicationSimpleWatchFaceService.java744
-rw-r--r--wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/provider/RandomNumberProviderService.java96
-rw-r--r--wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_complication_simple.pngbin0 -> 471002 bytes
-rw-r--r--wearable/wear/WatchFace/Wearable/src/main/res/drawable-xhdpi/complications_left_dial.pngbin0 -> 6692 bytes
-rw-r--r--wearable/wear/WatchFace/Wearable/src/main/res/drawable-xhdpi/complications_right_dial.pngbin0 -> 6704 bytes
-rw-r--r--wearable/wear/WatchFace/Wearable/src/main/res/layout/activity_complication_simple_config.xml38
-rw-r--r--wearable/wear/WatchFace/Wearable/src/main/res/layout/activity_complication_simple_list_item.xml34
-rw-r--r--wearable/wear/WatchFace/Wearable/src/main/res/values/arrays.xml26
-rw-r--r--wearable/wear/WatchFace/Wearable/src/main/res/values/strings.xml4
-rw-r--r--wearable/wear/WatchFace/gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--wearable/wear/WatchFace/screenshots/complication_simple_face.pngbin0 -> 472204 bytes
-rw-r--r--wearable/wear/WatchFace/template-params.xml6
14 files changed, 1204 insertions, 2 deletions
diff --git a/wearable/wear/WatchFace/Wearable/src/main/AndroidManifest.xml b/wearable/wear/WatchFace/Wearable/src/main/AndroidManifest.xml
index 88bb0336..a9222971 100644
--- a/wearable/wear/WatchFace/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/WatchFace/Wearable/src/main/AndroidManifest.xml
@@ -132,6 +132,42 @@
<category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
</intent-filter>
</service>
+
+ <service
+ android:name=".ComplicationSimpleWatchFaceService"
+ android:enabled="true"
+ android:label="@string/complication_simple"
+ android:permission="android.permission.BIND_WALLPAPER">
+ <meta-data
+ android:name="android.service.wallpaper"
+ android:resource="@xml/watch_face"/>
+ <meta-data
+ android:name="com.google.android.wearable.watchface.preview"
+ android:resource="@drawable/preview_complication_simple"/>
+ <meta-data
+ android:name="com.google.android.wearable.watchface.preview_circular"
+ android:resource="@drawable/preview_complication_simple"/>
+ <meta-data
+ android:name="com.google.android.wearable.watchface.wearableConfigurationAction"
+ android:value="com.example.android.wearable.watchface.CONFIG_COMPLICATION_SIMPLE"/>
+
+ <intent-filter>
+ <action android:name="android.service.wallpaper.WallpaperService"/>
+
+ <category android:name="com.google.android.wearable.watchface.category.WATCH_FACE"/>
+ </intent-filter>
+ </service>
+ <activity
+ android:name=".ComplicationSimpleConfigActivity"
+ android:label="@string/complication_simple">
+ <intent-filter>
+ <action android:name="com.example.android.wearable.watchface.CONFIG_COMPLICATION_SIMPLE"/>
+
+ <category android:name="com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
<service
android:name=".InteractiveWatchFaceService"
android:label="@string/interactive_name"
@@ -270,6 +306,35 @@
android:name=".CalendarWatchFacePermissionActivity"
android:label="@string/title_activity_calendar_watch_face_permission" >
</activity>
+
+ <service
+ android:name=".provider.RandomNumberProviderService"
+ android:label="@string/complications_provider_random_number"
+ android:icon="@drawable/ic_launcher">
+
+ <intent-filter>
+ <action android:name="android.support.wearable.complications.ACTION_COMPLICATION_UPDATE_REQUEST"/>
+ </intent-filter>
+
+ <meta-data
+ android:name="android.support.wearable.complications.SUPPORTED_TYPES"
+ android:value="RANGED_VALUE,SHORT_TEXT,LONG_TEXT"/>
+ <!--
+ When your complication data provider is active, UPDATE_PERIOD_SECONDS specifies how
+ often you want the system to check for updates to the data. In this case, the time is
+ specified to a relatively short 120 seconds, so we can observe the result of this code
+ lab. In everyday use, developers should consider intervals in the order of minutes.
+ Also, remember that this is only a guidance for the system. Android Wear may update less
+ frequently.
+
+ If your app needs to push updates instead of updating on a regular schedule, you should
+ set this value to 0 and use ProviderUpdateRequester.requestUpdate() to trigger an update
+ request when you need one.
+ -->
+ <meta-data
+ android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS"
+ android:value="120"/>
+ </service>
</application>
</manifest>
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/ComplicationSimpleConfigActivity.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/ComplicationSimpleConfigActivity.java
new file mode 100644
index 00000000..66e208c2
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/ComplicationSimpleConfigActivity.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+package com.example.android.wearable.watchface;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.wearable.complications.ProviderChooserIntent;
+import android.support.wearable.view.WearableListView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The watch-side config activity for {@link ComplicationSimpleWatchFaceService}, which
+ * allows for setting complications on the left and right of watch face.
+ */
+public class ComplicationSimpleConfigActivity extends Activity implements
+ WearableListView.ClickListener {
+
+ private static final String TAG = "CompSimpleConfig";
+
+ private static final int PROVIDER_CHOOSER_REQUEST_CODE = 1;
+
+ private WearableListView mWearableConfigListView;
+ private ConfigurationAdapter mAdapter;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_complication_simple_config);
+
+ mAdapter = new ConfigurationAdapter(getApplicationContext(), getComplicationItems());
+
+ mWearableConfigListView = (WearableListView) findViewById(R.id.wearable_list);
+ mWearableConfigListView.setAdapter(mAdapter);
+ mWearableConfigListView.setClickListener(this);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == PROVIDER_CHOOSER_REQUEST_CODE
+ && resultCode == RESULT_OK) {
+ finish();
+ }
+ }
+
+ @Override
+ public void onClick(WearableListView.ViewHolder viewHolder) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onClick()");
+ }
+
+ Integer tag = (Integer) viewHolder.itemView.getTag();
+ ComplicationItem complicationItem = mAdapter.getItem(tag);
+
+ startActivityForResult(ProviderChooserIntent.createProviderChooserIntent(
+ complicationItem.watchFace,
+ complicationItem.complicationId,
+ complicationItem.supportedTypes), PROVIDER_CHOOSER_REQUEST_CODE);
+ }
+
+ private List<ComplicationItem> getComplicationItems() {
+ ComponentName watchFace = new ComponentName(
+ getApplicationContext(), ComplicationSimpleWatchFaceService.class);
+
+ String[] complicationNames =
+ getResources().getStringArray(R.array.complication_simple_names);
+
+ int[] complicationIds = ComplicationSimpleWatchFaceService.COMPLICATION_IDS;
+
+ TypedArray icons = getResources().obtainTypedArray(R.array.complication_simple_icons);
+
+ List<ComplicationItem> items = new ArrayList<>();
+ for (int i = 0; i < complicationIds.length; i++) {
+ items.add(new ComplicationItem(watchFace,
+ complicationIds[i],
+ ComplicationSimpleWatchFaceService.COMPLICATION_SUPPORTED_TYPES[i],
+ icons.getDrawable(i),
+ complicationNames[i]));
+ }
+ return items;
+ }
+
+ @Override
+ public void onTopEmptyRegionClick() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onTopEmptyRegionClick()");
+ }
+ }
+
+ /*
+ * Inner class representing items of the ConfigurationAdapter (WearableListView.Adapter) class.
+ */
+ private final class ComplicationItem {
+ ComponentName watchFace;
+ int complicationId;
+ int[] supportedTypes;
+ Drawable icon;
+ String title;
+
+ public ComplicationItem(ComponentName watchFace, int complicationId, int[] supportedTypes,
+ Drawable icon, String title) {
+ this.watchFace = watchFace;
+ this.complicationId = complicationId;
+ this.supportedTypes = supportedTypes;
+ this.icon = icon;
+ this.title = title;
+ }
+ }
+
+ private static class ConfigurationAdapter extends WearableListView.Adapter {
+
+ private Context mContext;
+ private final LayoutInflater mInflater;
+ private List<ComplicationItem> mItems;
+
+
+ public ConfigurationAdapter (Context context, List<ComplicationItem> items) {
+ mContext = context;
+ mInflater = LayoutInflater.from(mContext);
+ mItems = items;
+ }
+
+ // Provides a reference to the type of views you're using
+ public static class ItemViewHolder extends WearableListView.ViewHolder {
+ private ImageView iconImageView;
+ private TextView textView;
+ public ItemViewHolder(View itemView) {
+ super(itemView);
+ iconImageView = (ImageView) itemView.findViewById(R.id.icon);
+ textView = (TextView) itemView.findViewById(R.id.name);
+ }
+ }
+
+ @Override
+ public WearableListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+
+ // Inflate custom layout for list items.
+ return new ItemViewHolder(
+ mInflater.inflate(R.layout.activity_complication_simple_list_item, null));
+ }
+
+ @Override
+ public void onBindViewHolder(WearableListView.ViewHolder holder, int position) {
+
+ ItemViewHolder itemHolder = (ItemViewHolder) holder;
+
+ ImageView imageView = itemHolder.iconImageView;
+ imageView.setImageDrawable(mItems.get(position).icon);
+
+ TextView textView = itemHolder.textView;
+ textView.setText(mItems.get(position).title);
+
+ holder.itemView.setTag(position);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mItems.size();
+ }
+
+ public ComplicationItem getItem(int position) {
+ return mItems.get(position);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/ComplicationSimpleWatchFaceService.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/ComplicationSimpleWatchFaceService.java
new file mode 100644
index 00000000..9eca2c33
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/ComplicationSimpleWatchFaceService.java
@@ -0,0 +1,744 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+package com.example.android.wearable.watchface;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.v7.graphics.Palette;
+import android.support.wearable.complications.ComplicationData;
+import android.support.wearable.complications.ComplicationText;
+import android.support.wearable.watchface.CanvasWatchFaceService;
+import android.support.wearable.watchface.WatchFaceService;
+import android.support.wearable.watchface.WatchFaceStyle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.SurfaceHolder;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Demonstrates two simple complications in a watch face.
+ */
+public class ComplicationSimpleWatchFaceService extends CanvasWatchFaceService {
+ private static final String TAG = "SimpleComplicationWF";
+
+ // Unique IDs for each complication.
+ private static final int LEFT_DIAL_COMPLICATION = 0;
+ private static final int RIGHT_DIAL_COMPLICATION = 1;
+
+ // Left and right complication IDs as array for Complication API.
+ public static final int[] COMPLICATION_IDS = {LEFT_DIAL_COMPLICATION, RIGHT_DIAL_COMPLICATION};
+
+ // Left and right dial supported types.
+ public static final int[][] COMPLICATION_SUPPORTED_TYPES = {
+ {ComplicationData.TYPE_SHORT_TEXT},
+ {ComplicationData.TYPE_SHORT_TEXT}
+ };
+
+ /*
+ * Update rate in milliseconds for interactive mode. We update once a second to advance the
+ * second hand.
+ */
+ private static final long INTERACTIVE_UPDATE_RATE_MS = TimeUnit.SECONDS.toMillis(1);
+
+ @Override
+ public Engine onCreateEngine() {
+ return new Engine();
+ }
+
+ private class Engine extends CanvasWatchFaceService.Engine {
+ private static final int MSG_UPDATE_TIME = 0;
+
+ private static final float COMPLICATION_TEXT_SIZE = 38f;
+ private static final int COMPLICATION_TAP_BUFFER = 40;
+
+ private static final float HOUR_STROKE_WIDTH = 5f;
+ private static final float MINUTE_STROKE_WIDTH = 3f;
+ private static final float SECOND_TICK_STROKE_WIDTH = 2f;
+
+ private static final float CENTER_GAP_AND_CIRCLE_RADIUS = 4f;
+
+ private static final int SHADOW_RADIUS = 6;
+
+ private Calendar mCalendar;
+ private boolean mRegisteredTimeZoneReceiver = false;
+ private boolean mMuteMode;
+
+ private int mWidth;
+ private int mHeight;
+ private float mCenterX;
+ private float mCenterY;
+
+ private float mSecondHandLength;
+ private float mMinuteHandLength;
+ private float mHourHandLength;
+
+ // Colors for all hands (hour, minute, seconds, ticks) based on photo loaded.
+ private int mWatchHandColor;
+ private int mWatchHandHighlightColor;
+ private int mWatchHandShadowColor;
+
+ private Paint mHourPaint;
+ private Paint mMinutePaint;
+ private Paint mSecondPaint;
+ private Paint mTickAndCirclePaint;
+
+ private Paint mBackgroundPaint;
+ private Bitmap mBackgroundBitmap;
+ private Bitmap mGrayBackgroundBitmap;
+
+ // Variables for painting Complications
+ private Paint mComplicationPaint;
+
+ /* To properly place each complication, we need their x and y coordinates. While the width
+ * may change from moment to moment based on the time, the height will not change, so we
+ * store it as a local variable and only calculate it only when the surface changes
+ * (onSurfaceChanged()).
+ */
+ private int mComplicationsY;
+
+ /* Maps active complication ids to the data for that complication. Note: Data will only be
+ * present if the user has chosen a provider via the settings activity for the watch face.
+ */
+ private SparseArray<ComplicationData> mActiveComplicationDataSparseArray;
+
+ private boolean mAmbient;
+ private boolean mLowBitAmbient;
+ private boolean mBurnInProtection;
+
+ private Rect mPeekCardBounds = new Rect();
+
+ private final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mCalendar.setTimeZone(TimeZone.getDefault());
+ invalidate();
+ }
+ };
+
+ // Handler to update the time once a second in interactive mode.
+ private final Handler mUpdateTimeHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "updating time");
+ }
+ invalidate();
+ if (shouldTimerBeRunning()) {
+ long timeMs = System.currentTimeMillis();
+ long delayMs = INTERACTIVE_UPDATE_RATE_MS
+ - (timeMs % INTERACTIVE_UPDATE_RATE_MS);
+ mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
+ }
+
+ }
+ };
+
+ @Override
+ public void onCreate(SurfaceHolder holder) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onCreate");
+ }
+ super.onCreate(holder);
+
+ mCalendar = Calendar.getInstance();
+
+ setWatchFaceStyle(new WatchFaceStyle.Builder(ComplicationSimpleWatchFaceService.this)
+ .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
+ .setAcceptsTapEvents(true)
+ .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
+ .setShowSystemUiTime(false)
+ .build());
+
+ initializeBackground();
+ initializeComplication();
+ initializeWatchFace();
+ }
+
+ private void initializeBackground() {
+ mBackgroundPaint = new Paint();
+ mBackgroundPaint.setColor(Color.BLACK);
+ mBackgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bg);
+ }
+
+ private void initializeComplication() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "initializeComplications()");
+ }
+ mActiveComplicationDataSparseArray = new SparseArray<>(COMPLICATION_IDS.length);
+
+ mComplicationPaint = new Paint();
+ mComplicationPaint.setColor(Color.WHITE);
+ mComplicationPaint.setTextSize(COMPLICATION_TEXT_SIZE);
+ mComplicationPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
+ mComplicationPaint.setAntiAlias(true);
+
+ setActiveComplications(COMPLICATION_IDS);
+ }
+
+ private void initializeWatchFace() {
+ /* Set defaults for colors */
+ mWatchHandColor = Color.WHITE;
+ mWatchHandHighlightColor = Color.RED;
+ mWatchHandShadowColor = Color.BLACK;
+
+ mHourPaint = new Paint();
+ mHourPaint.setColor(mWatchHandColor);
+ mHourPaint.setStrokeWidth(HOUR_STROKE_WIDTH);
+ mHourPaint.setAntiAlias(true);
+ mHourPaint.setStrokeCap(Paint.Cap.ROUND);
+ mHourPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
+
+ mMinutePaint = new Paint();
+ mMinutePaint.setColor(mWatchHandColor);
+ mMinutePaint.setStrokeWidth(MINUTE_STROKE_WIDTH);
+ mMinutePaint.setAntiAlias(true);
+ mMinutePaint.setStrokeCap(Paint.Cap.ROUND);
+ mMinutePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
+
+ mSecondPaint = new Paint();
+ mSecondPaint.setColor(mWatchHandHighlightColor);
+ mSecondPaint.setStrokeWidth(SECOND_TICK_STROKE_WIDTH);
+ mSecondPaint.setAntiAlias(true);
+ mSecondPaint.setStrokeCap(Paint.Cap.ROUND);
+ mSecondPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
+
+ mTickAndCirclePaint = new Paint();
+ mTickAndCirclePaint.setColor(mWatchHandColor);
+ mTickAndCirclePaint.setStrokeWidth(SECOND_TICK_STROKE_WIDTH);
+ mTickAndCirclePaint.setAntiAlias(true);
+ mTickAndCirclePaint.setStyle(Paint.Style.STROKE);
+ mTickAndCirclePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
+
+ // Asynchronous call extract colors from background image to improve watch face style.
+ Palette.from(mBackgroundBitmap).generate(
+ new Palette.PaletteAsyncListener() {
+ public void onGenerated(Palette palette) {
+ /*
+ * Sometimes, palette is unable to generate a color palette
+ * so we need to check that we have one.
+ */
+ if (palette != null) {
+ Log.d("onGenerated", palette.toString());
+ mWatchHandColor = palette.getVibrantColor(Color.WHITE);
+ mWatchHandShadowColor = palette.getDarkMutedColor(Color.BLACK);
+ updateWatchHandStyle();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onDestroy() {
+ mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
+ super.onDestroy();
+ }
+
+ @Override
+ public void onPropertiesChanged(Bundle properties) {
+ super.onPropertiesChanged(properties);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onPropertiesChanged: low-bit ambient = " + mLowBitAmbient);
+ }
+
+ mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
+ mBurnInProtection = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, false);
+ }
+
+ /*
+ * Called when there is updated data for a complication id.
+ */
+ @Override
+ public void onComplicationDataUpdate(
+ int complicationId, ComplicationData complicationData) {
+ Log.d(TAG, "onComplicationDataUpdate() id: " + complicationId);
+
+ // Adds/updates active complication data in the array.
+ mActiveComplicationDataSparseArray.put(complicationId, complicationData);
+ invalidate();
+ }
+
+ @Override
+ public void onTapCommand(int tapType, int x, int y, long eventTime) {
+ Log.d(TAG, "OnTapCommand()");
+ switch (tapType) {
+ case TAP_TYPE_TAP:
+ int tappedComplicationId = getTappedComplicationId(x, y);
+ if (tappedComplicationId != -1) {
+ onComplicationTap(tappedComplicationId);
+ }
+ break;
+ }
+ }
+
+ /*
+ * Determines if tap inside a complication area or returns -1.
+ */
+ private int getTappedComplicationId(int x, int y) {
+ ComplicationData complicationData;
+ long currentTimeMillis = System.currentTimeMillis();
+
+ for (int i = 0; i < COMPLICATION_IDS.length; i++) {
+ complicationData = mActiveComplicationDataSparseArray.get(COMPLICATION_IDS[i]);
+
+ if ((complicationData != null)
+ && (complicationData.isActive(currentTimeMillis))
+ && (complicationData.getType() != ComplicationData.TYPE_NOT_CONFIGURED)
+ && (complicationData.getType() != ComplicationData.TYPE_EMPTY)) {
+
+ Rect complicationBoundingRect = new Rect(0, 0, 0, 0);
+
+ switch (COMPLICATION_IDS[i]) {
+ case LEFT_DIAL_COMPLICATION:
+ complicationBoundingRect.set(
+ 0, // left
+ mComplicationsY - COMPLICATION_TAP_BUFFER, // top
+ (mWidth / 2), // right
+ ((int) COMPLICATION_TEXT_SIZE // bottom
+ + mComplicationsY
+ + COMPLICATION_TAP_BUFFER));
+ break;
+
+ case RIGHT_DIAL_COMPLICATION:
+ complicationBoundingRect.set(
+ (mWidth / 2), // left
+ mComplicationsY - COMPLICATION_TAP_BUFFER, // top
+ mWidth, // right
+ ((int) COMPLICATION_TEXT_SIZE // bottom
+ + mComplicationsY
+ + COMPLICATION_TAP_BUFFER));
+ break;
+ }
+
+ if (complicationBoundingRect.width() > 0) {
+ if (complicationBoundingRect.contains(x, y)) {
+ return COMPLICATION_IDS[i];
+ }
+ } else {
+ Log.e(TAG, "Not a recognized complication id.");
+ }
+ }
+ }
+ return -1;
+ }
+
+ /*
+ * Fires PendingIntent associated with complication (if it has one).
+ */
+ private void onComplicationTap(int complicationId) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onComplicationTap()");
+ }
+ ComplicationData complicationData =
+ mActiveComplicationDataSparseArray.get(complicationId);
+
+ if ((complicationData != null) && (complicationData.getTapAction() != null)) {
+ try {
+ complicationData.getTapAction().send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "On complication tap action error " + e);
+ }
+ invalidate();
+ } else {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "No PendingIntent for complication " + complicationId + ".");
+ }
+ }
+ }
+
+ @Override
+ public void onTimeTick() {
+ super.onTimeTick();
+ invalidate();
+ }
+
+ @Override
+ public void onAmbientModeChanged(boolean inAmbientMode) {
+ super.onAmbientModeChanged(inAmbientMode);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onAmbientModeChanged: " + inAmbientMode);
+ }
+ mAmbient = inAmbientMode;
+
+ updateWatchHandStyle();
+
+ // Updates complication style
+ mComplicationPaint.setAntiAlias(!inAmbientMode);
+
+ // Check and trigger whether or not timer should be running (only in active mode).
+ updateTimer();
+ }
+
+ private void updateWatchHandStyle(){
+ if (mAmbient){
+ mHourPaint.setColor(Color.WHITE);
+ mMinutePaint.setColor(Color.WHITE);
+ mSecondPaint.setColor(Color.WHITE);
+ mTickAndCirclePaint.setColor(Color.WHITE);
+
+ mHourPaint.setAntiAlias(false);
+ mMinutePaint.setAntiAlias(false);
+ mSecondPaint.setAntiAlias(false);
+ mTickAndCirclePaint.setAntiAlias(false);
+
+ mHourPaint.clearShadowLayer();
+ mMinutePaint.clearShadowLayer();
+ mSecondPaint.clearShadowLayer();
+ mTickAndCirclePaint.clearShadowLayer();
+
+ } else {
+ mHourPaint.setColor(mWatchHandColor);
+ mMinutePaint.setColor(mWatchHandColor);
+ mSecondPaint.setColor(mWatchHandHighlightColor);
+ mTickAndCirclePaint.setColor(mWatchHandColor);
+
+ mHourPaint.setAntiAlias(true);
+ mMinutePaint.setAntiAlias(true);
+ mSecondPaint.setAntiAlias(true);
+ mTickAndCirclePaint.setAntiAlias(true);
+
+ mHourPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
+ mMinutePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
+ mSecondPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
+ mTickAndCirclePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
+ }
+ }
+
+ @Override
+ public void onInterruptionFilterChanged(int interruptionFilter) {
+ super.onInterruptionFilterChanged(interruptionFilter);
+ boolean inMuteMode = (interruptionFilter == WatchFaceService.INTERRUPTION_FILTER_NONE);
+
+ /* Dim display in mute mode. */
+ if (mMuteMode != inMuteMode) {
+ mMuteMode = inMuteMode;
+ mHourPaint.setAlpha(inMuteMode ? 100 : 255);
+ mMinutePaint.setAlpha(inMuteMode ? 100 : 255);
+ mSecondPaint.setAlpha(inMuteMode ? 80 : 255);
+ invalidate();
+ }
+ }
+
+ @Override
+ public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ super.onSurfaceChanged(holder, format, width, height);
+
+ // Used for complications
+ mWidth = width;
+ mHeight = height;
+
+ /*
+ * Find the coordinates of the center point on the screen, and ignore the window
+ * insets, so that, on round watches with a "chin", the watch face is centered on the
+ * entire screen, not just the usable portion.
+ */
+ mCenterX = mWidth / 2f;
+ mCenterY = mHeight / 2f;
+
+ /*
+ * Since the height of the complications text does not change, we only have to
+ * recalculate when the surface changes.
+ */
+ mComplicationsY = (int) ((mHeight / 2) + (mComplicationPaint.getTextSize() / 2));
+
+ /*
+ * Calculate lengths of different hands based on watch screen size.
+ */
+ mSecondHandLength = (float) (mCenterX * 0.875);
+ mMinuteHandLength = (float) (mCenterX * 0.75);
+ mHourHandLength = (float) (mCenterX * 0.5);
+
+
+ /* Scale loaded background image (more efficient) if surface dimensions change. */
+ float scale = ((float) width) / (float) mBackgroundBitmap.getWidth();
+
+ mBackgroundBitmap = Bitmap.createScaledBitmap(mBackgroundBitmap,
+ (int) (mBackgroundBitmap.getWidth() * scale),
+ (int) (mBackgroundBitmap.getHeight() * scale), true);
+
+ /*
+ * Create a gray version of the image only if it will look nice on the device in
+ * ambient mode. That means we don't want devices that support burn-in
+ * protection (slight movements in pixels, not great for images going all the way to
+ * edges) and low ambient mode (degrades image quality).
+ *
+ * Also, if your watch face will know about all images ahead of time (users aren't
+ * selecting their own photos for the watch face), it will be more
+ * efficient to create a black/white version (png, etc.) and load that when you need it.
+ */
+ if (!mBurnInProtection && !mLowBitAmbient) {
+ initGrayBackgroundBitmap();
+ }
+ }
+
+ private void initGrayBackgroundBitmap() {
+ mGrayBackgroundBitmap = Bitmap.createBitmap(
+ mBackgroundBitmap.getWidth(),
+ mBackgroundBitmap.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(mGrayBackgroundBitmap);
+ Paint grayPaint = new Paint();
+ ColorMatrix colorMatrix = new ColorMatrix();
+ colorMatrix.setSaturation(0);
+ ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
+ grayPaint.setColorFilter(filter);
+ canvas.drawBitmap(mBackgroundBitmap, 0, 0, grayPaint);
+ }
+
+ @Override
+ public void onDraw(Canvas canvas, Rect bounds) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onDraw");
+ }
+ long now = System.currentTimeMillis();
+ mCalendar.setTimeInMillis(now);
+
+ drawBackground(canvas);
+ drawComplications(canvas, now);
+ drawWatchFace(canvas);
+
+
+ }
+
+ private void drawBackground(Canvas canvas) {
+ if (mAmbient && (mLowBitAmbient || mBurnInProtection)) {
+ canvas.drawColor(Color.BLACK);
+ } else if (mAmbient) {
+ canvas.drawBitmap(mGrayBackgroundBitmap, 0, 0, mBackgroundPaint);
+ } else {
+ canvas.drawBitmap(mBackgroundBitmap, 0, 0, mBackgroundPaint);
+ }
+ }
+
+ private void drawComplications(Canvas canvas, long currentTimeMillis) {
+ ComplicationData complicationData;
+
+ for (int i = 0; i < COMPLICATION_IDS.length; i++) {
+
+ complicationData = mActiveComplicationDataSparseArray.get(COMPLICATION_IDS[i]);
+
+ if ((complicationData != null)
+ && (complicationData.isActive(currentTimeMillis))
+ && (complicationData.getType() == ComplicationData.TYPE_SHORT_TEXT)) {
+
+ ComplicationText mainText = complicationData.getShortText();
+ ComplicationText subText = complicationData.getShortTitle();
+
+ CharSequence complicationMessage =
+ mainText.getText(getApplicationContext(), currentTimeMillis);
+
+ /* In most cases you would want the subText (Title) under the mainText (Text),
+ * but to keep it simple for the code lab, we are concatenating them all on one
+ * line.
+ */
+ if (subText != null) {
+ complicationMessage = TextUtils.concat(
+ complicationMessage,
+ " ",
+ subText.getText(getApplicationContext(), currentTimeMillis));
+ }
+
+ //Log.d(TAG, "Comp id: " + COMPLICATION_IDS[i] + "\t" + complicationMessage);
+ double textWidth =
+ mComplicationPaint.measureText(
+ complicationMessage,
+ 0,
+ complicationMessage.length());
+
+ int complicationsX;
+
+ if (COMPLICATION_IDS[i] == LEFT_DIAL_COMPLICATION) {
+ complicationsX = (int) ((mWidth / 2) - textWidth) / 2;
+ } else {
+ // RIGHT_DIAL_COMPLICATION calculations
+ int offset = (int) ((mWidth / 2) - textWidth) / 2;
+ complicationsX = (mWidth / 2) + offset;
+ }
+
+ canvas.drawText(
+ complicationMessage,
+ 0,
+ complicationMessage.length(),
+ complicationsX,
+ mComplicationsY,
+ mComplicationPaint);
+ }
+ }
+ }
+
+ private void drawWatchFace(Canvas canvas) {
+ /*
+ * Draw ticks. Usually you will want to bake this directly into the photo, but in
+ * cases where you want to allow users to select their own photos, this dynamically
+ * creates them on top of the photo.
+ */
+ float innerTickRadius = mCenterX - 10;
+ float outerTickRadius = mCenterX;
+ for (int tickIndex = 0; tickIndex < 12; tickIndex++) {
+ float tickRot = (float) (tickIndex * Math.PI * 2 / 12);
+ float innerX = (float) Math.sin(tickRot) * innerTickRadius;
+ float innerY = (float) -Math.cos(tickRot) * innerTickRadius;
+ float outerX = (float) Math.sin(tickRot) * outerTickRadius;
+ float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
+ canvas.drawLine(mCenterX + innerX, mCenterY + innerY,
+ mCenterX + outerX, mCenterY + outerY, mTickAndCirclePaint);
+ }
+
+ /*
+ * These calculations reflect the rotation in degrees per unit of time, e.g.,
+ * 360 / 60 = 6 and 360 / 12 = 30.
+ */
+ final float seconds =
+ (mCalendar.get(Calendar.SECOND) + mCalendar.get(Calendar.MILLISECOND) / 1000f);
+ final float secondsRotation = seconds * 6f;
+
+ final float minutesRotation = mCalendar.get(Calendar.MINUTE) * 6f;
+
+ final float hourHandOffset = mCalendar.get(Calendar.MINUTE) / 2f;
+ final float hoursRotation = (mCalendar.get(Calendar.HOUR) * 30) + hourHandOffset;
+
+ /*
+ * Save the canvas state before we can begin to rotate it.
+ */
+ canvas.save();
+
+ canvas.rotate(hoursRotation, mCenterX, mCenterY);
+ canvas.drawLine(
+ mCenterX,
+ mCenterY - CENTER_GAP_AND_CIRCLE_RADIUS,
+ mCenterX,
+ mCenterY - mHourHandLength,
+ mHourPaint);
+
+ canvas.rotate(minutesRotation - hoursRotation, mCenterX, mCenterY);
+ canvas.drawLine(
+ mCenterX,
+ mCenterY - CENTER_GAP_AND_CIRCLE_RADIUS,
+ mCenterX,
+ mCenterY - mMinuteHandLength,
+ mMinutePaint);
+
+ /*
+ * Ensure the "seconds" hand is drawn only when we are in interactive mode.
+ * Otherwise, we only update the watch face once a minute.
+ */
+ if (!mAmbient) {
+ canvas.rotate(secondsRotation - minutesRotation, mCenterX, mCenterY);
+ canvas.drawLine(
+ mCenterX,
+ mCenterY - CENTER_GAP_AND_CIRCLE_RADIUS,
+ mCenterX,
+ mCenterY - mSecondHandLength,
+ mSecondPaint);
+
+ }
+ canvas.drawCircle(
+ mCenterX,
+ mCenterY,
+ CENTER_GAP_AND_CIRCLE_RADIUS,
+ mTickAndCirclePaint);
+
+ /* Restore the canvas' original orientation. */
+ canvas.restore();
+
+ /* Draw rectangle behind peek card in ambient mode to improve readability. */
+ if (mAmbient) {
+ canvas.drawRect(mPeekCardBounds, mBackgroundPaint);
+ }
+ }
+
+ @Override
+ public void onVisibilityChanged(boolean visible) {
+ super.onVisibilityChanged(visible);
+
+ if (visible) {
+ registerReceiver();
+ // Update time zone in case it changed while we weren't visible.
+ mCalendar.setTimeZone(TimeZone.getDefault());
+ invalidate();
+ } else {
+ unregisterReceiver();
+ }
+
+ /* Check and trigger whether or not timer should be running (only in active mode). */
+ updateTimer();
+ }
+
+ @Override
+ public void onPeekCardPositionUpdate(Rect rect) {
+ super.onPeekCardPositionUpdate(rect);
+ mPeekCardBounds.set(rect);
+ }
+
+ private void registerReceiver() {
+ if (mRegisteredTimeZoneReceiver) {
+ return;
+ }
+ mRegisteredTimeZoneReceiver = true;
+ IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
+ ComplicationSimpleWatchFaceService.this.registerReceiver(mTimeZoneReceiver, filter);
+ }
+
+ private void unregisterReceiver() {
+ if (!mRegisteredTimeZoneReceiver) {
+ return;
+ }
+ mRegisteredTimeZoneReceiver = false;
+ ComplicationSimpleWatchFaceService.this.unregisterReceiver(mTimeZoneReceiver);
+ }
+
+ /**
+ * Starts/stops the {@link #mUpdateTimeHandler} timer based on the state of the watch face.
+ */
+ private void updateTimer() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "updateTimer");
+ }
+ mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
+ if (shouldTimerBeRunning()) {
+ mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME);
+ }
+ }
+
+ /**
+ * Returns whether the {@link #mUpdateTimeHandler} timer should be running. The timer
+ * should only run in active mode.
+ */
+ private boolean shouldTimerBeRunning() {
+ return isVisible() && !mAmbient;
+ }
+ }
+} \ No newline at end of file
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/provider/RandomNumberProviderService.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/provider/RandomNumberProviderService.java
new file mode 100644
index 00000000..916f90fd
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/provider/RandomNumberProviderService.java
@@ -0,0 +1,96 @@
+package com.example.android.wearable.watchface.provider;
+
+import android.support.wearable.complications.ComplicationData;
+import android.support.wearable.complications.ComplicationManager;
+import android.support.wearable.complications.ComplicationProviderService;
+import android.support.wearable.complications.ComplicationText;
+import android.util.Log;
+
+import java.util.Locale;
+
+/**
+ * Example Watch Face Complication data provider provides a random number on every update.
+ */
+public class RandomNumberProviderService extends ComplicationProviderService {
+
+ private static final String TAG = "RandomNumberProvider";
+
+ /*
+ * Called when a complication has been activated. The method is for any one-time
+ * (per complication) set-up.
+ *
+ * You can continue sending data for the active complicationId until onComplicationDeactivated()
+ * is called.
+ */
+ @Override
+ public void onComplicationActivated(
+ int complicationId, int dataType, ComplicationManager complicationManager) {
+ Log.d(TAG, "onComplicationActivated(): " + complicationId);
+ super.onComplicationActivated(complicationId, dataType, complicationManager);
+ }
+
+ /*
+ * Called when the complication needs updated data from your provider. There are four scenarios
+ * when this will happen:
+ *
+ * 1. An active watch face complication is changed to use this provider
+ * 2. A complication using this provider becomes active
+ * 3. The period of time you specified in the manifest has elapsed (UPDATE_PERIOD_SECONDS)
+ * 4. You triggered an update from your own class via the
+ * ProviderUpdateRequester.requestUpdate() method.
+ */
+ @Override
+ public void onComplicationUpdate(
+ int complicationId, int dataType, ComplicationManager complicationManager) {
+ Log.d(TAG, "onComplicationUpdate()");
+
+
+ // Retrieve your data, in this case, we simply create a random number to display.
+ int randomNumber = (int) Math.floor(Math.random() * 10);
+
+ String randomNumberText =
+ String.format(Locale.getDefault(), "%d!", randomNumber);
+
+ ComplicationData complicationData = null;
+
+ switch (dataType) {
+ case ComplicationData.TYPE_RANGED_VALUE:
+ complicationData = new ComplicationData.Builder(ComplicationData.TYPE_RANGED_VALUE)
+ .setValue(randomNumber)
+ .setMinValue(0)
+ .setMaxValue(10)
+ .setShortText(ComplicationText.plainText(randomNumberText))
+ .build();
+ break;
+ case ComplicationData.TYPE_SHORT_TEXT:
+ complicationData = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
+ .setShortText(ComplicationText.plainText(randomNumberText))
+ .build();
+ break;
+ case ComplicationData.TYPE_LONG_TEXT:
+ complicationData = new ComplicationData.Builder(ComplicationData.TYPE_LONG_TEXT)
+ .setLongText(
+ ComplicationText.plainText("Random Number: " + randomNumberText))
+ .build();
+ break;
+ default:
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(TAG, "Unexpected complication type " + dataType);
+ }
+ }
+
+ if (complicationData != null) {
+ complicationManager.updateComplicationData(complicationId, complicationData);
+ }
+ }
+
+ /*
+ * Called when the complication has been deactivated. If you are updating the complication
+ * manager outside of this class with updates, you will want to update your class to stop.
+ */
+ @Override
+ public void onComplicationDeactivated(int complicationId) {
+ Log.d(TAG, "onComplicationDeactivated(): " + complicationId);
+ super.onComplicationDeactivated(complicationId);
+ }
+} \ No newline at end of file
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_complication_simple.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_complication_simple.png
new file mode 100644
index 00000000..48d25ef8
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_complication_simple.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xhdpi/complications_left_dial.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xhdpi/complications_left_dial.png
new file mode 100644
index 00000000..93bb31ca
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xhdpi/complications_left_dial.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xhdpi/complications_right_dial.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xhdpi/complications_right_dial.png
new file mode 100644
index 00000000..5db3c982
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xhdpi/complications_right_dial.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/layout/activity_complication_simple_config.xml b/wearable/wear/WatchFace/Wearable/src/main/res/layout/activity_complication_simple_config.xml
new file mode 100644
index 00000000..01f5cac7
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/layout/activity_complication_simple_config.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@color/dark_grey"
+ android:paddingTop="4dp">
+
+ <TextView
+ android:id="@+id/header"
+ android:layout_width="match_parent"
+ android:gravity="center"
+ android:text="@string/complication_simple_config_name"
+ android:textSize="20sp"
+ android:layout_height="wrap_content"/>
+
+ <android.support.wearable.view.WearableListView
+ android:id="@+id/wearable_list"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent">
+ </android.support.wearable.view.WearableListView>
+</LinearLayout> \ No newline at end of file
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/layout/activity_complication_simple_list_item.xml b/wearable/wear/WatchFace/Wearable/src/main/res/layout/activity_complication_simple_list_item.xml
new file mode 100644
index 00000000..e9952718
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/layout/activity_complication_simple_list_item.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 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.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center">
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:textColor="@color/white"
+ android:textSize="18sp"/>
+</LinearLayout> \ No newline at end of file
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/values/arrays.xml b/wearable/wear/WatchFace/Wearable/src/main/res/values/arrays.xml
new file mode 100644
index 00000000..a2e37acb
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/values/arrays.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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-array name="complication_simple_names">
+ <item>Left dial</item>
+ <item>Right dial</item>
+ </string-array>
+ <array name="complication_simple_icons">
+ <item>@drawable/complications_left_dial</item>
+ <item>@drawable/complications_right_dial</item>
+ </array>
+</resources> \ No newline at end of file
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/values/strings.xml b/wearable/wear/WatchFace/Wearable/src/main/res/values/strings.xml
index 4090995d..e4b27173 100644
--- a/wearable/wear/WatchFace/Wearable/src/main/res/values/strings.xml
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/values/strings.xml
@@ -18,6 +18,7 @@
<string name="opengl_name">Sample OpenGL</string>
<string name="interactive_name">Sample Interactive</string>
<string name="analog_name">Sample Analog</string>
+ <string name="complication_simple">Sample Complication Simple</string>
<string name="sweep_name">Sample Sweep</string>
<string name="card_bounds_name">Sample Card Bounds</string>
<string name="digital_name">Sample Digital</string>
@@ -42,6 +43,9 @@
<string name="title_activity_calendar_watch_face_permission">Calendar Permission Activity</string>
<string name="calendar_permission_text">WatchFace requires Calendar access.</string>
+ <string name="complication_simple_config_name">Configuration</string>
+ <string name="complications_provider_random_number">Random Number</string>
+
<!-- TODO: this should be shared (needs covering all the samples with Gradle build model) -->
<string name="color_black">Black</string>
<string name="color_blue">Blue</string>
diff --git a/wearable/wear/WatchFace/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/WatchFace/gradle/wrapper/gradle-wrapper.properties
index 1f89809f..128405e9 100644
--- a/wearable/wear/WatchFace/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/WatchFace/gradle/wrapper/gradle-wrapper.properties
@@ -1,4 +1,4 @@
-#Fri Apr 15 15:26:25 PDT 2016
+#Thu May 12 20:18:40 PDT 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/wearable/wear/WatchFace/screenshots/complication_simple_face.png b/wearable/wear/WatchFace/screenshots/complication_simple_face.png
new file mode 100644
index 00000000..f756b144
--- /dev/null
+++ b/wearable/wear/WatchFace/screenshots/complication_simple_face.png
Binary files differ
diff --git a/wearable/wear/WatchFace/template-params.xml b/wearable/wear/WatchFace/template-params.xml
index e339fe26..f4aa1059 100644
--- a/wearable/wear/WatchFace/template-params.xml
+++ b/wearable/wear/WatchFace/template-params.xml
@@ -26,8 +26,12 @@
<targetSdkVersion>23</targetSdkVersion>
<targetSdkVersionWear>23</targetSdkVersionWear>
+ <dependency_wearable>com.google.android.gms:play-services-wearable</dependency_wearable>
<dependency_wearable>com.android.support:palette-v7:23.1.1</dependency_wearable>
- <dependency>com.google.android.support:wearable:1.3.0</dependency>
+ <dependency>com.google.android.support:wearable:1.4.0</dependency>
+
+ <dependency_wearable>com.google.android.support:wearable:2.0.0-alpha1</dependency_wearable>
+
<dependency>com.google.android.gms:play-services-fitness</dependency>
<dependency_wearable>com.google.android.gms:play-services-fitness</dependency_wearable>