am 81556478: Sync sample prebuilts for mnc-dev
* commit '8155647801c9da4070bc0a044ec73afc126f4b88': Sync sample prebuilts for mnc-dev
@@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.example.android.common.activities;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v4.app.FragmentActivity;
|
|
||||||
|
|
||||||
import com.example.android.common.logger.Log;
|
|
||||||
import com.example.android.common.logger.LogWrapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base launcher activity, to handle most of the common plumbing for samples.
|
|
||||||
*/
|
|
||||||
public class SampleActivityBase extends FragmentActivity {
|
|
||||||
|
|
||||||
public static final String TAG = "SampleActivityBase";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
initializeLogging();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Set up targets to receive log data */
|
|
||||||
public void initializeLogging() {
|
|
||||||
// Using Log, front-end to the logging chain, emulates android.util.log method signatures.
|
|
||||||
// Wraps Android's native log framework
|
|
||||||
LogWrapper logWrapper = new LogWrapper();
|
|
||||||
Log.setLogNode(logWrapper);
|
|
||||||
|
|
||||||
Log.i(TAG, "Ready");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
page.tags="MyCloud"
|
page.tags="StorageProvider"
|
||||||
sample.group=Content
|
sample.group=Content
|
||||||
@jd:body
|
@jd:body
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">MyCloud</string>
|
<string name="app_name">StorageProvider</string>
|
||||||
<string name="intro_message">
|
<string name="intro_message">
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class MainActivity extends SampleActivityBase {
|
|||||||
|
|
||||||
public static final String TAG = "MainActivity";
|
public static final String TAG = "MainActivity";
|
||||||
|
|
||||||
public static final String FRAGTAG = "MyCloudFragment";
|
public static final String FRAGTAG = "StorageProviderFragment";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -45,7 +45,7 @@ public class MainActivity extends SampleActivityBase {
|
|||||||
|
|
||||||
if (getSupportFragmentManager().findFragmentByTag(FRAGTAG) == null ) {
|
if (getSupportFragmentManager().findFragmentByTag(FRAGTAG) == null ) {
|
||||||
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
|
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
|
||||||
MyCloudFragment fragment = new MyCloudFragment();
|
StorageProviderFragment fragment = new StorageProviderFragment();
|
||||||
transaction.add(fragment, FRAGTAG);
|
transaction.add(fragment, FRAGTAG);
|
||||||
transaction.commit();
|
transaction.commit();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,10 +56,10 @@
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".TiltWatchFaceConfigActivity"
|
android:name=".OpenGLWatchFaceConfigActivity"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.example.android.wearable.watchface.CONFIG_TILT" />
|
<action android:name="com.example.android.wearable.watchface.CONFIG_OPENGL" />
|
||||||
<category android:name="com.google.android.wearable.watchface.category.COMPANION_CONFIGURATION" />
|
<category android:name="com.google.android.wearable.watchface.category.COMPANION_CONFIGURATION" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/label"
|
android:id="@+id/label"
|
||||||
android:text="@string/tilt_config_text"
|
android:text="@string/opengl_config_text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
@@ -23,8 +23,8 @@
|
|||||||
|
|
||||||
This sample demonstrates how to create watch faces for android wear and includes a phone app
|
This sample demonstrates how to create watch faces for android wear and includes a phone app
|
||||||
and a wearable app. The wearable app has a variety of watch faces including analog, digital,
|
and a wearable app. The wearable app has a variety of watch faces including analog, digital,
|
||||||
opengl, calendar, etc. It also includes a watch-side configuration example. The phone app
|
opengl, calendar, interactive, etc. It also includes a watch-side configuration example.
|
||||||
includes a phone-side configuration example.
|
The phone app includes a phone-side configuration example.
|
||||||
|
|
||||||
|
|
||||||
]]>
|
]]>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="analog_config_text">This is the config activity for the Analog and Card Bounds watch faces</string>
|
<string name="analog_config_text">This is the config activity for the Analog and Card Bounds watch faces</string>
|
||||||
<string name="digital_config_text">Digital watch face configuration</string>
|
<string name="digital_config_text">Digital watch face configuration</string>
|
||||||
<string name="tilt_config_text">Tilt watch face configuration</string>
|
<string name="opengl_config_text">OpenGL watch face configuration</string>
|
||||||
<string name="digital_config_background">Background</string>
|
<string name="digital_config_background">Background</string>
|
||||||
<string name="digital_config_hours">Hours</string>
|
<string name="digital_config_hours">Hours</string>
|
||||||
<string name="digital_config_minutes">Minutes</string>
|
<string name="digital_config_minutes">Minutes</string>
|
||||||
|
|||||||
@@ -22,16 +22,21 @@ import android.os.Bundle;
|
|||||||
import android.support.wearable.companion.WatchFaceCompanion;
|
import android.support.wearable.companion.WatchFaceCompanion;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
public class TiltWatchFaceConfigActivity extends Activity {
|
/**
|
||||||
|
* The phone-side config activity for {@code OpenGLWatchFaceService}. The
|
||||||
|
* activity ({@code OpenGLWatchFaceWearableConfigActivity}) doesn't offer any configurations, but
|
||||||
|
* provides a template to add your own.
|
||||||
|
*/
|
||||||
|
public class OpenGLWatchFaceConfigActivity extends Activity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_tilt_watch_face_config);
|
setContentView(R.layout.activity_opengl_watch_face_config);
|
||||||
|
|
||||||
ComponentName name =
|
ComponentName name =
|
||||||
getIntent().getParcelableExtra(WatchFaceCompanion.EXTRA_WATCH_FACE_COMPONENT);
|
getIntent().getParcelableExtra(WatchFaceCompanion.EXTRA_WATCH_FACE_COMPONENT);
|
||||||
TextView label = (TextView)findViewById(R.id.label);
|
TextView label = (TextView) findViewById(R.id.label);
|
||||||
label.setText(label.getText() + " (" + name.getClassName() + ")");
|
label.setText(label.getText() + " (" + name.getClassName() + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,21 +76,21 @@
|
|||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".TiltWatchFaceService"
|
android:name=".OpenGLWatchFaceService"
|
||||||
android:label="@string/tilt_name"
|
android:label="@string/opengl_name"
|
||||||
android:permission="android.permission.BIND_WALLPAPER" >
|
android:permission="android.permission.BIND_WALLPAPER" >
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.service.wallpaper"
|
android:name="android.service.wallpaper"
|
||||||
android:resource="@xml/watch_face" />
|
android:resource="@xml/watch_face" />
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.wearable.watchface.preview"
|
android:name="com.google.android.wearable.watchface.preview"
|
||||||
android:resource="@drawable/preview_tilt" />
|
android:resource="@drawable/preview_opengl" />
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.wearable.watchface.preview_circular"
|
android:name="com.google.android.wearable.watchface.preview_circular"
|
||||||
android:resource="@drawable/preview_tilt_circular" />
|
android:resource="@drawable/preview_opengl_circular" />
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.wearable.watchface.companionConfigurationAction"
|
android:name="com.google.android.wearable.watchface.companionConfigurationAction"
|
||||||
android:value="com.example.android.wearable.watchface.CONFIG_TILT" />
|
android:value="com.example.android.wearable.watchface.CONFIG_OPENGL" />
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.service.wallpaper.WallpaperService" />
|
<action android:name="android.service.wallpaper.WallpaperService" />
|
||||||
@@ -120,6 +120,27 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".InteractiveWatchFaceService"
|
||||||
|
android:label="@string/interactive_name"
|
||||||
|
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_interactive" />
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.android.wearable.watchface.preview_circular"
|
||||||
|
android:resource="@drawable/preview_interactive_circular" />
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.service.wallpaper.WallpaperService" />
|
||||||
|
<category
|
||||||
|
android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".DigitalWatchFaceService"
|
android:name=".DigitalWatchFaceService"
|
||||||
android:label="@string/digital_name"
|
android:label="@string/digital_name"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 159 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 264 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 212 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
@@ -25,4 +25,11 @@
|
|||||||
<dimen name="digital_line_height">25dp</dimen>
|
<dimen name="digital_line_height">25dp</dimen>
|
||||||
<dimen name="digital_config_color_picker_item_margin">32dp</dimen>
|
<dimen name="digital_config_color_picker_item_margin">32dp</dimen>
|
||||||
<dimen name="content_padding_start">12dp</dimen>
|
<dimen name="content_padding_start">12dp</dimen>
|
||||||
|
<dimen name="interactive_text_size">20dp</dimen>
|
||||||
|
<dimen name="interactive_text_size_round">25dp</dimen>
|
||||||
|
<dimen name="interactive_x_offset">15dp</dimen>
|
||||||
|
<dimen name="interactive_x_offset_round">25dp</dimen>
|
||||||
|
<dimen name="interactive_y_offset">72dp</dimen>
|
||||||
|
<dimen name="interactive_y_offset_round">84dp</dimen>
|
||||||
|
<dimen name="interactive_line_height">25dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
-->
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">WatchFace</string>
|
<string name="app_name">WatchFace</string>
|
||||||
<string name="tilt_name">Sample Tilt</string>
|
<string name="opengl_name">Sample OpenGL</string>
|
||||||
|
<string name="interactive_name">Sample Interactive</string>
|
||||||
<string name="analog_name">Sample Analog</string>
|
<string name="analog_name">Sample Analog</string>
|
||||||
<string name="sweep_name">Sample Sweep</string>
|
<string name="sweep_name">Sample Sweep</string>
|
||||||
<string name="card_bounds_name">Sample Card Bounds</string>
|
<string name="card_bounds_name">Sample Card Bounds</string>
|
||||||
|
|||||||
@@ -20,16 +20,18 @@ import android.content.BroadcastReceiver;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.ColorMatrix;
|
||||||
|
import android.graphics.ColorMatrixColorFilter;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.support.v7.graphics.Palette;
|
||||||
import android.support.wearable.watchface.CanvasWatchFaceService;
|
import android.support.wearable.watchface.CanvasWatchFaceService;
|
||||||
import android.support.wearable.watchface.WatchFaceService;
|
import android.support.wearable.watchface.WatchFaceService;
|
||||||
import android.support.wearable.watchface.WatchFaceStyle;
|
import android.support.wearable.watchface.WatchFaceStyle;
|
||||||
@@ -50,7 +52,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
public class AnalogWatchFaceService extends CanvasWatchFaceService {
|
public class AnalogWatchFaceService extends CanvasWatchFaceService {
|
||||||
private static final String TAG = "AnalogWatchFaceService";
|
private static final String TAG = "AnalogWatchFaceService";
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Update rate in milliseconds for interactive mode. We update once a second to advance the
|
* Update rate in milliseconds for interactive mode. We update once a second to advance the
|
||||||
* second hand.
|
* second hand.
|
||||||
*/
|
*/
|
||||||
@@ -62,25 +64,62 @@ public class AnalogWatchFaceService extends CanvasWatchFaceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class Engine extends CanvasWatchFaceService.Engine {
|
private class Engine extends CanvasWatchFaceService.Engine {
|
||||||
static final int MSG_UPDATE_TIME = 0;
|
private static final int MSG_UPDATE_TIME = 0;
|
||||||
|
|
||||||
static final float TWO_PI = (float) Math.PI * 2f;
|
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;
|
||||||
|
|
||||||
Paint mHourPaint;
|
private static final float CENTER_GAP_AND_CIRCLE_RADIUS = 4f;
|
||||||
Paint mMinutePaint;
|
|
||||||
Paint mSecondPaint;
|
|
||||||
Paint mTickPaint;
|
|
||||||
boolean mMute;
|
|
||||||
Calendar mCalendar;
|
|
||||||
|
|
||||||
/** Handler to update the time once a second in interactive mode. */
|
private static final int SHADOW_RADIUS = 6;
|
||||||
final Handler mUpdateTimeHandler = new Handler() {
|
|
||||||
|
private Calendar mCalendar;
|
||||||
|
private boolean mRegisteredTimeZoneReceiver = false;
|
||||||
|
private boolean mMuteMode;
|
||||||
|
|
||||||
|
private float mCenterX;
|
||||||
|
private float mCenterY;
|
||||||
|
|
||||||
|
private float mSecondHandLength;
|
||||||
|
private float sMinuteHandLength;
|
||||||
|
private float sHourHandLength;
|
||||||
|
|
||||||
|
/* Colors for all hands (hour, minute, seconds, ticks) based on photo loaded. */
|
||||||
|
private int mWatchHandColor;
|
||||||
|
private int mWatchHandHightlightColor;
|
||||||
|
private int mWatchHandShadowColor;
|
||||||
|
|
||||||
|
private Paint mHourPaint;
|
||||||
|
private Paint mMinutePaint;
|
||||||
|
private Paint mSecondPaint;
|
||||||
|
private Paint mTickAndCirclePaint;
|
||||||
|
|
||||||
|
private Paint mBackgroundPaint;
|
||||||
|
private Bitmap mBackgroundBitmap;
|
||||||
|
private Bitmap mGrayBackgroundBitmap;
|
||||||
|
|
||||||
|
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
|
@Override
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
switch (message.what) {
|
|
||||||
case MSG_UPDATE_TIME:
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
Log.d(TAG, "updating time");
|
||||||
Log.v(TAG, "updating time");
|
|
||||||
}
|
}
|
||||||
invalidate();
|
invalidate();
|
||||||
if (shouldTimerBeRunning()) {
|
if (shouldTimerBeRunning()) {
|
||||||
@@ -89,29 +128,10 @@ public class AnalogWatchFaceService extends CanvasWatchFaceService {
|
|||||||
- (timeMs % INTERACTIVE_UPDATE_RATE_MS);
|
- (timeMs % INTERACTIVE_UPDATE_RATE_MS);
|
||||||
mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
|
mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
mCalendar.setTimeZone(TimeZone.getDefault());
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
boolean mRegisteredTimeZoneReceiver = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the display supports fewer bits for each color in ambient mode. When true, we
|
|
||||||
* disable anti-aliasing in ambient mode.
|
|
||||||
*/
|
|
||||||
boolean mLowBitAmbient;
|
|
||||||
|
|
||||||
Bitmap mBackgroundBitmap;
|
|
||||||
Bitmap mBackgroundScaledBitmap;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(SurfaceHolder holder) {
|
public void onCreate(SurfaceHolder holder) {
|
||||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
@@ -125,32 +145,61 @@ public class AnalogWatchFaceService extends CanvasWatchFaceService {
|
|||||||
.setShowSystemUiTime(false)
|
.setShowSystemUiTime(false)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
Resources resources = AnalogWatchFaceService.this.getResources();
|
mBackgroundPaint = new Paint();
|
||||||
Drawable backgroundDrawable = resources.getDrawable(R.drawable.bg, null /* theme */);
|
mBackgroundPaint.setColor(Color.BLACK);
|
||||||
mBackgroundBitmap = ((BitmapDrawable) backgroundDrawable).getBitmap();
|
mBackgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bg);
|
||||||
|
|
||||||
|
/* Set defaults for colors */
|
||||||
|
mWatchHandColor = Color.WHITE;
|
||||||
|
mWatchHandHightlightColor = Color.RED;
|
||||||
|
mWatchHandShadowColor = Color.BLACK;
|
||||||
|
|
||||||
mHourPaint = new Paint();
|
mHourPaint = new Paint();
|
||||||
mHourPaint.setARGB(255, 200, 200, 200);
|
mHourPaint.setColor(mWatchHandColor);
|
||||||
mHourPaint.setStrokeWidth(5.f);
|
mHourPaint.setStrokeWidth(HOUR_STROKE_WIDTH);
|
||||||
mHourPaint.setAntiAlias(true);
|
mHourPaint.setAntiAlias(true);
|
||||||
mHourPaint.setStrokeCap(Paint.Cap.ROUND);
|
mHourPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
|
mHourPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
|
||||||
|
|
||||||
mMinutePaint = new Paint();
|
mMinutePaint = new Paint();
|
||||||
mMinutePaint.setARGB(255, 200, 200, 200);
|
mMinutePaint.setColor(mWatchHandColor);
|
||||||
mMinutePaint.setStrokeWidth(3.f);
|
mMinutePaint.setStrokeWidth(MINUTE_STROKE_WIDTH);
|
||||||
mMinutePaint.setAntiAlias(true);
|
mMinutePaint.setAntiAlias(true);
|
||||||
mMinutePaint.setStrokeCap(Paint.Cap.ROUND);
|
mMinutePaint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
|
mMinutePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
|
||||||
|
|
||||||
mSecondPaint = new Paint();
|
mSecondPaint = new Paint();
|
||||||
mSecondPaint.setARGB(255, 255, 0, 0);
|
mSecondPaint.setColor(mWatchHandHightlightColor);
|
||||||
mSecondPaint.setStrokeWidth(2.f);
|
mSecondPaint.setStrokeWidth(SECOND_TICK_STROKE_WIDTH);
|
||||||
mSecondPaint.setAntiAlias(true);
|
mSecondPaint.setAntiAlias(true);
|
||||||
mSecondPaint.setStrokeCap(Paint.Cap.ROUND);
|
mSecondPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
|
mSecondPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
|
||||||
|
|
||||||
mTickPaint = new Paint();
|
mTickAndCirclePaint = new Paint();
|
||||||
mTickPaint.setARGB(100, 255, 255, 255);
|
mTickAndCirclePaint.setColor(mWatchHandColor);
|
||||||
mTickPaint.setStrokeWidth(2.f);
|
mTickAndCirclePaint.setStrokeWidth(SECOND_TICK_STROKE_WIDTH);
|
||||||
mTickPaint.setAntiAlias(true);
|
mTickAndCirclePaint.setAntiAlias(true);
|
||||||
|
mTickAndCirclePaint.setStyle(Paint.Style.STROKE);
|
||||||
|
mTickAndCirclePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
|
||||||
|
|
||||||
|
/* Extract colors from background image to improve watchface style. */
|
||||||
|
Palette.generateAsync(
|
||||||
|
mBackgroundBitmap,
|
||||||
|
new Palette.PaletteAsyncListener() {
|
||||||
|
@Override
|
||||||
|
public void onGenerated(Palette palette) {
|
||||||
|
if (palette != null) {
|
||||||
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
|
Log.d(TAG, "Palette: " + palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
mWatchHandHightlightColor = palette.getVibrantColor(Color.RED);
|
||||||
|
mWatchHandColor = palette.getLightVibrantColor(Color.WHITE);
|
||||||
|
mWatchHandShadowColor = palette.getDarkMutedColor(Color.BLACK);
|
||||||
|
updateWatchHandStyle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
mCalendar = Calendar.getInstance();
|
mCalendar = Calendar.getInstance();
|
||||||
}
|
}
|
||||||
@@ -164,18 +213,17 @@ public class AnalogWatchFaceService extends CanvasWatchFaceService {
|
|||||||
@Override
|
@Override
|
||||||
public void onPropertiesChanged(Bundle properties) {
|
public void onPropertiesChanged(Bundle properties) {
|
||||||
super.onPropertiesChanged(properties);
|
super.onPropertiesChanged(properties);
|
||||||
mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
|
|
||||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
Log.d(TAG, "onPropertiesChanged: low-bit ambient = " + mLowBitAmbient);
|
Log.d(TAG, "onPropertiesChanged: low-bit ambient = " + mLowBitAmbient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
|
||||||
|
mBurnInProtection = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTimeTick() {
|
public void onTimeTick() {
|
||||||
super.onTimeTick();
|
super.onTimeTick();
|
||||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
|
||||||
Log.d(TAG, "onTimeTick: ambient = " + isInAmbientMode());
|
|
||||||
}
|
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,26 +233,57 @@ public class AnalogWatchFaceService extends CanvasWatchFaceService {
|
|||||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
Log.d(TAG, "onAmbientModeChanged: " + inAmbientMode);
|
Log.d(TAG, "onAmbientModeChanged: " + inAmbientMode);
|
||||||
}
|
}
|
||||||
if (mLowBitAmbient) {
|
mAmbient = inAmbientMode;
|
||||||
boolean antiAlias = !inAmbientMode;
|
|
||||||
mHourPaint.setAntiAlias(antiAlias);
|
|
||||||
mMinutePaint.setAntiAlias(antiAlias);
|
|
||||||
mSecondPaint.setAntiAlias(antiAlias);
|
|
||||||
mTickPaint.setAntiAlias(antiAlias);
|
|
||||||
}
|
|
||||||
invalidate();
|
|
||||||
|
|
||||||
// Whether the timer should be running depends on whether we're in ambient mode (as well
|
updateWatchHandStyle();
|
||||||
// as whether we're visible), so we may need to start or stop the timer.
|
|
||||||
|
/* Check and trigger whether or not timer should be running (only in active mode). */
|
||||||
updateTimer();
|
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(mWatchHandHightlightColor);
|
||||||
|
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
|
@Override
|
||||||
public void onInterruptionFilterChanged(int interruptionFilter) {
|
public void onInterruptionFilterChanged(int interruptionFilter) {
|
||||||
super.onInterruptionFilterChanged(interruptionFilter);
|
super.onInterruptionFilterChanged(interruptionFilter);
|
||||||
boolean inMuteMode = (interruptionFilter == WatchFaceService.INTERRUPTION_FILTER_NONE);
|
boolean inMuteMode = (interruptionFilter == WatchFaceService.INTERRUPTION_FILTER_NONE);
|
||||||
if (mMute != inMuteMode) {
|
|
||||||
mMute = inMuteMode;
|
/* Dim display in mute mode. */
|
||||||
|
if (mMuteMode != inMuteMode) {
|
||||||
|
mMuteMode = inMuteMode;
|
||||||
mHourPaint.setAlpha(inMuteMode ? 100 : 255);
|
mHourPaint.setAlpha(inMuteMode ? 100 : 255);
|
||||||
mMinutePaint.setAlpha(inMuteMode ? 100 : 255);
|
mMinutePaint.setAlpha(inMuteMode ? 100 : 255);
|
||||||
mSecondPaint.setAlpha(inMuteMode ? 80 : 255);
|
mSecondPaint.setAlpha(inMuteMode ? 80 : 255);
|
||||||
@@ -214,92 +293,179 @@ public class AnalogWatchFaceService extends CanvasWatchFaceService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||||
if (mBackgroundScaledBitmap == null
|
|
||||||
|| mBackgroundScaledBitmap.getWidth() != width
|
|
||||||
|| mBackgroundScaledBitmap.getHeight() != height) {
|
|
||||||
mBackgroundScaledBitmap = Bitmap.createScaledBitmap(mBackgroundBitmap,
|
|
||||||
width, height, true /* filter */);
|
|
||||||
}
|
|
||||||
super.onSurfaceChanged(holder, format, width, height);
|
super.onSurfaceChanged(holder, format, width, 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 = width / 2f;
|
||||||
|
mCenterY = height / 2f;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate lengths of different hands based on watch screen size.
|
||||||
|
*/
|
||||||
|
mSecondHandLength = (float) (mCenterX * 0.875);
|
||||||
|
sMinuteHandLength = (float) (mCenterX * 0.75);
|
||||||
|
sHourHandLength = (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
|
@Override
|
||||||
public void onDraw(Canvas canvas, Rect bounds) {
|
public void onDraw(Canvas canvas, Rect bounds) {
|
||||||
mCalendar.setTimeInMillis(System.currentTimeMillis());
|
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||||
|
Log.v(TAG, "onDraw");
|
||||||
|
}
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
mCalendar.setTimeInMillis(now);
|
||||||
|
|
||||||
int width = bounds.width();
|
if (mAmbient && (mLowBitAmbient || mBurnInProtection)) {
|
||||||
int height = bounds.height();
|
canvas.drawColor(Color.BLACK);
|
||||||
|
} else if (mAmbient) {
|
||||||
|
canvas.drawBitmap(mGrayBackgroundBitmap, 0, 0, mBackgroundPaint);
|
||||||
|
} else {
|
||||||
|
canvas.drawBitmap(mBackgroundBitmap, 0, 0, mBackgroundPaint);
|
||||||
|
}
|
||||||
|
|
||||||
// Draw the background, scaled to fit.
|
/*
|
||||||
canvas.drawBitmap(mBackgroundScaledBitmap, 0, 0, null);
|
* 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
|
||||||
// Find the center. Ignore the window insets so that, on round watches with a
|
* creates them on top of the photo.
|
||||||
// "chin", the watch face is centered on the entire screen, not just the usable
|
*/
|
||||||
// portion.
|
float innerTickRadius = mCenterX - 10;
|
||||||
float centerX = width / 2f;
|
float outerTickRadius = mCenterX;
|
||||||
float centerY = height / 2f;
|
|
||||||
|
|
||||||
// Draw the ticks.
|
|
||||||
float innerTickRadius = centerX - 10;
|
|
||||||
float outerTickRadius = centerX;
|
|
||||||
for (int tickIndex = 0; tickIndex < 12; tickIndex++) {
|
for (int tickIndex = 0; tickIndex < 12; tickIndex++) {
|
||||||
float tickRot = tickIndex * TWO_PI / 12;
|
float tickRot = (float) (tickIndex * Math.PI * 2 / 12);
|
||||||
float innerX = (float) Math.sin(tickRot) * innerTickRadius;
|
float innerX = (float) Math.sin(tickRot) * innerTickRadius;
|
||||||
float innerY = (float) -Math.cos(tickRot) * innerTickRadius;
|
float innerY = (float) -Math.cos(tickRot) * innerTickRadius;
|
||||||
float outerX = (float) Math.sin(tickRot) * outerTickRadius;
|
float outerX = (float) Math.sin(tickRot) * outerTickRadius;
|
||||||
float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
|
float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
|
||||||
canvas.drawLine(centerX + innerX, centerY + innerY,
|
canvas.drawLine(mCenterX + innerX, mCenterY + innerY,
|
||||||
centerX + outerX, centerY + outerY, mTickPaint);
|
mCenterX + outerX, mCenterY + outerY, mTickAndCirclePaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
float seconds =
|
/*
|
||||||
mCalendar.get(Calendar.SECOND) + mCalendar.get(Calendar.MILLISECOND) / 1000f;
|
* These calculations reflect the rotation in degrees per unit of time, e.g.,
|
||||||
float secRot = seconds / 60f * TWO_PI;
|
* 360 / 60 = 6 and 360 / 12 = 30.
|
||||||
float minutes = mCalendar.get(Calendar.MINUTE) + seconds / 60f;
|
*/
|
||||||
float minRot = minutes / 60f * TWO_PI;
|
final float seconds =
|
||||||
float hours = mCalendar.get(Calendar.HOUR) + minutes / 60f;
|
(mCalendar.get(Calendar.SECOND) + mCalendar.get(Calendar.MILLISECOND) / 1000f);
|
||||||
float hrRot = hours / 12f * TWO_PI;
|
final float secondsRotation = seconds * 6f;
|
||||||
|
|
||||||
float secLength = centerX - 20;
|
final float minutesRotation = mCalendar.get(Calendar.MINUTE) * 6f;
|
||||||
float minLength = centerX - 40;
|
|
||||||
float hrLength = centerX - 80;
|
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 - sHourHandLength,
|
||||||
|
mHourPaint);
|
||||||
|
|
||||||
|
canvas.rotate(minutesRotation - hoursRotation, mCenterX, mCenterY);
|
||||||
|
canvas.drawLine(
|
||||||
|
mCenterX,
|
||||||
|
mCenterY - CENTER_GAP_AND_CIRCLE_RADIUS,
|
||||||
|
mCenterX,
|
||||||
|
mCenterY - sMinuteHandLength,
|
||||||
|
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);
|
||||||
|
|
||||||
if (!isInAmbientMode()) {
|
|
||||||
float secX = (float) Math.sin(secRot) * secLength;
|
|
||||||
float secY = (float) -Math.cos(secRot) * secLength;
|
|
||||||
canvas.drawLine(centerX, centerY, centerX + secX, centerY + secY, mSecondPaint);
|
|
||||||
}
|
}
|
||||||
|
canvas.drawCircle(
|
||||||
|
mCenterX,
|
||||||
|
mCenterY,
|
||||||
|
CENTER_GAP_AND_CIRCLE_RADIUS,
|
||||||
|
mTickAndCirclePaint);
|
||||||
|
|
||||||
float minX = (float) Math.sin(minRot) * minLength;
|
/* Restore the canvas' original orientation. */
|
||||||
float minY = (float) -Math.cos(minRot) * minLength;
|
canvas.restore();
|
||||||
canvas.drawLine(centerX, centerY, centerX + minX, centerY + minY, mMinutePaint);
|
|
||||||
|
|
||||||
float hrX = (float) Math.sin(hrRot) * hrLength;
|
/* Draw rectangle behind peek card in ambient mode to improve readability. */
|
||||||
float hrY = (float) -Math.cos(hrRot) * hrLength;
|
if (mAmbient) {
|
||||||
canvas.drawLine(centerX, centerY, centerX + hrX, centerY + hrY, mHourPaint);
|
canvas.drawRect(mPeekCardBounds, mBackgroundPaint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onVisibilityChanged(boolean visible) {
|
public void onVisibilityChanged(boolean visible) {
|
||||||
super.onVisibilityChanged(visible);
|
super.onVisibilityChanged(visible);
|
||||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
|
||||||
Log.d(TAG, "onVisibilityChanged: " + visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
registerReceiver();
|
registerReceiver();
|
||||||
|
/* Update time zone in case it changed while we weren't visible. */
|
||||||
// Update time zone in case it changed while we weren't visible.
|
|
||||||
mCalendar.setTimeZone(TimeZone.getDefault());
|
mCalendar.setTimeZone(TimeZone.getDefault());
|
||||||
|
invalidate();
|
||||||
} else {
|
} else {
|
||||||
unregisterReceiver();
|
unregisterReceiver();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whether the timer should be running depends on whether we're visible (as well as
|
/* Check and trigger whether or not timer should be running (only in active mode). */
|
||||||
// whether we're in ambient mode), so we may need to start or stop the timer.
|
|
||||||
updateTimer();
|
updateTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPeekCardPositionUpdate(Rect rect) {
|
||||||
|
super.onPeekCardPositionUpdate(rect);
|
||||||
|
mPeekCardBounds.set(rect);
|
||||||
|
}
|
||||||
|
|
||||||
private void registerReceiver() {
|
private void registerReceiver() {
|
||||||
if (mRegisteredTimeZoneReceiver) {
|
if (mRegisteredTimeZoneReceiver) {
|
||||||
return;
|
return;
|
||||||
@@ -318,8 +484,7 @@ public class AnalogWatchFaceService extends CanvasWatchFaceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the {@link #mUpdateTimeHandler} timer if it should be running and isn't currently
|
* Starts/stops the {@link #mUpdateTimeHandler} timer based on the state of the watch face.
|
||||||
* or stops it if it shouldn't be running but currently is.
|
|
||||||
*/
|
*/
|
||||||
private void updateTimer() {
|
private void updateTimer() {
|
||||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
@@ -332,12 +497,11 @@ public class AnalogWatchFaceService extends CanvasWatchFaceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the {@link #mUpdateTimeHandler} timer should be running. The timer should
|
* Returns whether the {@link #mUpdateTimeHandler} timer should be running. The timer
|
||||||
* only run when we're visible and in interactive mode.
|
* should only run in active mode.
|
||||||
*/
|
*/
|
||||||
private boolean shouldTimerBeRunning() {
|
private boolean shouldTimerBeRunning() {
|
||||||
return isVisible() && !isInAmbientMode();
|
return isVisible() && !mAmbient;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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.content.res.Resources;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.wearable.watchface.CanvasWatchFaceService;
|
||||||
|
import android.support.wearable.watchface.WatchFaceStyle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
import android.view.WindowInsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demostrates interactive watch face capabilities, i.e., touching the display and registering
|
||||||
|
* three different events: touch, touch-cancel and tap. The watch face UI will show the count of
|
||||||
|
* these events as they occur. See the {@code onTapCommand} below.
|
||||||
|
*/
|
||||||
|
public class InteractiveWatchFaceService extends CanvasWatchFaceService {
|
||||||
|
|
||||||
|
private static final String TAG = "InteractiveWatchFace";
|
||||||
|
|
||||||
|
private static final Typeface BOLD_TYPEFACE =
|
||||||
|
Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
|
||||||
|
private static final Typeface NORMAL_TYPEFACE =
|
||||||
|
Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Engine onCreateEngine() {
|
||||||
|
return new Engine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Engine extends CanvasWatchFaceService.Engine {
|
||||||
|
|
||||||
|
private Paint mTextPaint;
|
||||||
|
private final Paint mPeekCardBackgroundPaint = new Paint();
|
||||||
|
|
||||||
|
private float mXOffset;
|
||||||
|
private float mYOffset;
|
||||||
|
private float mTextSpacingHeight;
|
||||||
|
private int mScreenTextColor = Color.WHITE;
|
||||||
|
|
||||||
|
private int mTouchCommandTotal;
|
||||||
|
private int mTouchCancelCommandTotal;
|
||||||
|
private int mTapCommandTotal;
|
||||||
|
|
||||||
|
private int mTouchCoordinateX;
|
||||||
|
private int mTouchCoordinateY;
|
||||||
|
|
||||||
|
private final Rect mCardBounds = new Rect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the display supports fewer bits for each color in ambient mode. When true, we
|
||||||
|
* disable anti-aliasing in ambient mode.
|
||||||
|
*/
|
||||||
|
private boolean mLowBitAmbient;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(SurfaceHolder holder) {
|
||||||
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
|
Log.d(TAG, "onCreate");
|
||||||
|
}
|
||||||
|
super.onCreate(holder);
|
||||||
|
|
||||||
|
/** Accepts tap events via WatchFaceStyle (setAcceptsTapEvents(true)). */
|
||||||
|
setWatchFaceStyle(new WatchFaceStyle.Builder(InteractiveWatchFaceService.this)
|
||||||
|
.setCardPeekMode(WatchFaceStyle.PEEK_MODE_VARIABLE)
|
||||||
|
.setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
|
||||||
|
.setShowSystemUiTime(false)
|
||||||
|
.setAcceptsTapEvents(true)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
Resources resources = InteractiveWatchFaceService.this.getResources();
|
||||||
|
mTextSpacingHeight = resources.getDimension(R.dimen.interactive_text_size);
|
||||||
|
|
||||||
|
mTextPaint = new Paint();
|
||||||
|
mTextPaint.setColor(mScreenTextColor);
|
||||||
|
mTextPaint.setTypeface(BOLD_TYPEFACE);
|
||||||
|
mTextPaint.setAntiAlias(true);
|
||||||
|
|
||||||
|
mTouchCommandTotal = 0;
|
||||||
|
mTouchCancelCommandTotal = 0;
|
||||||
|
mTapCommandTotal = 0;
|
||||||
|
|
||||||
|
mTouchCoordinateX = 0;
|
||||||
|
mTouchCoordinateX = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplyWindowInsets(WindowInsets insets) {
|
||||||
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
|
Log.d(TAG, "onApplyWindowInsets: " + (insets.isRound() ? "round" : "square"));
|
||||||
|
}
|
||||||
|
super.onApplyWindowInsets(insets);
|
||||||
|
|
||||||
|
/** Loads offsets / text size based on device type (square vs. round). */
|
||||||
|
Resources resources = InteractiveWatchFaceService.this.getResources();
|
||||||
|
boolean isRound = insets.isRound();
|
||||||
|
mXOffset = resources.getDimension(
|
||||||
|
isRound ? R.dimen.interactive_x_offset_round : R.dimen.interactive_x_offset);
|
||||||
|
mYOffset = resources.getDimension(
|
||||||
|
isRound ? R.dimen.interactive_y_offset_round : R.dimen.interactive_y_offset);
|
||||||
|
|
||||||
|
float textSize = resources.getDimension(
|
||||||
|
isRound ? R.dimen.interactive_text_size_round : R.dimen.interactive_text_size);
|
||||||
|
|
||||||
|
mTextPaint.setTextSize(textSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPeekCardPositionUpdate(Rect bounds) {
|
||||||
|
super.onPeekCardPositionUpdate(bounds);
|
||||||
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
|
Log.d(TAG, "onPeekCardPositionUpdate: " + bounds);
|
||||||
|
}
|
||||||
|
super.onPeekCardPositionUpdate(bounds);
|
||||||
|
if (!bounds.equals(mCardBounds)) {
|
||||||
|
mCardBounds.set(bounds);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPropertiesChanged(Bundle properties) {
|
||||||
|
super.onPropertiesChanged(properties);
|
||||||
|
|
||||||
|
boolean burnInProtection = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, false);
|
||||||
|
mTextPaint.setTypeface(burnInProtection ? NORMAL_TYPEFACE : BOLD_TYPEFACE);
|
||||||
|
|
||||||
|
mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
|
||||||
|
|
||||||
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
|
Log.d(TAG, "onPropertiesChanged: burn-in protection = " + burnInProtection
|
||||||
|
+ ", low-bit ambient = " + mLowBitAmbient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAmbientModeChanged(boolean inAmbientMode) {
|
||||||
|
super.onAmbientModeChanged(inAmbientMode);
|
||||||
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
|
Log.d(TAG, "onAmbientModeChanged: " + inAmbientMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mLowBitAmbient) {
|
||||||
|
boolean antiAlias = !inAmbientMode;
|
||||||
|
mTextPaint.setAntiAlias(antiAlias);
|
||||||
|
}
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Captures tap event (and tap type) and increments correct tap type total.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onTapCommand(int tapType, int x, int y, long eventTime) {
|
||||||
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
|
Log.d(TAG, "Tap Command: " + tapType);
|
||||||
|
}
|
||||||
|
|
||||||
|
mTouchCoordinateX = x;
|
||||||
|
mTouchCoordinateY = y;
|
||||||
|
|
||||||
|
switch(tapType) {
|
||||||
|
case TAP_TYPE_TOUCH:
|
||||||
|
mTouchCommandTotal++;
|
||||||
|
break;
|
||||||
|
case TAP_TYPE_TOUCH_CANCEL:
|
||||||
|
mTouchCancelCommandTotal++;
|
||||||
|
break;
|
||||||
|
case TAP_TYPE_TAP:
|
||||||
|
mTapCommandTotal++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDraw(Canvas canvas, Rect bounds) {
|
||||||
|
/** Draws background */
|
||||||
|
canvas.drawColor(Color.BLACK);
|
||||||
|
|
||||||
|
canvas.drawText(
|
||||||
|
"TAP: " + String.valueOf(mTapCommandTotal),
|
||||||
|
mXOffset,
|
||||||
|
mYOffset,
|
||||||
|
mTextPaint);
|
||||||
|
|
||||||
|
canvas.drawText(
|
||||||
|
"CANCEL: " + String.valueOf(mTouchCancelCommandTotal),
|
||||||
|
mXOffset,
|
||||||
|
mYOffset + mTextSpacingHeight,
|
||||||
|
mTextPaint);
|
||||||
|
|
||||||
|
canvas.drawText(
|
||||||
|
"TOUCH: " + String.valueOf(mTouchCommandTotal),
|
||||||
|
mXOffset,
|
||||||
|
mYOffset + (mTextSpacingHeight * 2),
|
||||||
|
mTextPaint);
|
||||||
|
|
||||||
|
canvas.drawText(
|
||||||
|
"X, Y: " + mTouchCoordinateX + ", " + mTouchCoordinateY,
|
||||||
|
mXOffset,
|
||||||
|
mYOffset + (mTextSpacingHeight * 3),
|
||||||
|
mTextPaint
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Covers area under peek card */
|
||||||
|
if (isInAmbientMode()) {
|
||||||
|
canvas.drawRect(mCardBounds, mPeekCardBackgroundPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,9 +37,9 @@ import java.util.concurrent.TimeUnit;
|
|||||||
* {@link Gles2ColoredTriangleList}s. The camera moves around in interactive mode and stops moving
|
* {@link Gles2ColoredTriangleList}s. The camera moves around in interactive mode and stops moving
|
||||||
* when the watch enters ambient mode.
|
* when the watch enters ambient mode.
|
||||||
*/
|
*/
|
||||||
public class TiltWatchFaceService extends Gles2WatchFaceService {
|
public class OpenGLWatchFaceService extends Gles2WatchFaceService {
|
||||||
|
|
||||||
private static final String TAG = "TiltWatchFaceService";
|
private static final String TAG = "OpenGLWatchFaceService";
|
||||||
|
|
||||||
/** Expected frame rate in interactive mode. */
|
/** Expected frame rate in interactive mode. */
|
||||||
private static final long FPS = 60;
|
private static final long FPS = 60;
|
||||||
@@ -129,7 +129,7 @@ public class TiltWatchFaceService extends Gles2WatchFaceService {
|
|||||||
Log.d(TAG, "onCreate");
|
Log.d(TAG, "onCreate");
|
||||||
}
|
}
|
||||||
super.onCreate(surfaceHolder);
|
super.onCreate(surfaceHolder);
|
||||||
setWatchFaceStyle(new WatchFaceStyle.Builder(TiltWatchFaceService.this)
|
setWatchFaceStyle(new WatchFaceStyle.Builder(OpenGLWatchFaceService.this)
|
||||||
.setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
|
.setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
|
||||||
.setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
|
.setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
|
||||||
.setStatusBarGravity(Gravity.RIGHT | Gravity.TOP)
|
.setStatusBarGravity(Gravity.RIGHT | Gravity.TOP)
|
||||||
@@ -395,7 +395,7 @@ public class TiltWatchFaceService extends Gles2WatchFaceService {
|
|||||||
}
|
}
|
||||||
mRegisteredTimeZoneReceiver = true;
|
mRegisteredTimeZoneReceiver = true;
|
||||||
IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
|
IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
|
||||||
TiltWatchFaceService.this.registerReceiver(mTimeZoneReceiver, filter);
|
OpenGLWatchFaceService.this.registerReceiver(mTimeZoneReceiver, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void unregisterReceiver() {
|
private void unregisterReceiver() {
|
||||||
@@ -403,7 +403,7 @@ public class TiltWatchFaceService extends Gles2WatchFaceService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mRegisteredTimeZoneReceiver = false;
|
mRegisteredTimeZoneReceiver = false;
|
||||||
TiltWatchFaceService.this.unregisterReceiver(mTimeZoneReceiver);
|
OpenGLWatchFaceService.this.unregisterReceiver(mTimeZoneReceiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -20,14 +20,16 @@ import android.content.BroadcastReceiver;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.ColorMatrix;
|
||||||
|
import android.graphics.ColorMatrixColorFilter;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.graphics.Palette;
|
||||||
import android.support.wearable.watchface.CanvasWatchFaceService;
|
import android.support.wearable.watchface.CanvasWatchFaceService;
|
||||||
import android.support.wearable.watchface.WatchFaceService;
|
import android.support.wearable.watchface.WatchFaceService;
|
||||||
import android.support.wearable.watchface.WatchFaceStyle;
|
import android.support.wearable.watchface.WatchFaceStyle;
|
||||||
@@ -45,6 +47,7 @@ import java.util.TimeZone;
|
|||||||
* {@link AnalogWatchFaceService} is similar but has a ticking second hand.
|
* {@link AnalogWatchFaceService} is similar but has a ticking second hand.
|
||||||
*/
|
*/
|
||||||
public class SweepWatchFaceService extends CanvasWatchFaceService {
|
public class SweepWatchFaceService extends CanvasWatchFaceService {
|
||||||
|
|
||||||
private static final String TAG = "SweepWatchFaceService";
|
private static final String TAG = "SweepWatchFaceService";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -53,32 +56,53 @@ public class SweepWatchFaceService extends CanvasWatchFaceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class Engine extends CanvasWatchFaceService.Engine {
|
private class Engine extends CanvasWatchFaceService.Engine {
|
||||||
static final float TWO_PI = (float) Math.PI * 2f;
|
|
||||||
|
|
||||||
Paint mHourPaint;
|
private static final float HOUR_STROKE_WIDTH = 5f;
|
||||||
Paint mMinutePaint;
|
private static final float MINUTE_STROKE_WIDTH = 3f;
|
||||||
Paint mSecondPaint;
|
private static final float SECOND_TICK_STROKE_WIDTH = 2f;
|
||||||
Paint mTickPaint;
|
|
||||||
boolean mMute;
|
|
||||||
Calendar mCalendar;
|
|
||||||
|
|
||||||
final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
|
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 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 mWatchHandHightlightColor;
|
||||||
|
private int mWatchHandShadowColor;
|
||||||
|
|
||||||
|
private Paint mHourPaint;
|
||||||
|
private Paint mMinutePaint;
|
||||||
|
private Paint mSecondPaint;
|
||||||
|
private Paint mTickAndCirclePaint;
|
||||||
|
|
||||||
|
private Paint mBackgroundPaint;
|
||||||
|
private Bitmap mBackgroundBitmap;
|
||||||
|
private Bitmap mGrayBackgroundBitmap;
|
||||||
|
|
||||||
|
private boolean mAmbient;
|
||||||
|
private boolean mLowBitAmbient;
|
||||||
|
private boolean mBurnInProtection;
|
||||||
|
|
||||||
|
private Rect mPeekCardBounds = new Rect();
|
||||||
|
|
||||||
|
private final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
mCalendar.setTimeZone(TimeZone.getDefault());
|
mCalendar.setTimeZone(TimeZone.getDefault());
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
boolean mRegisteredTimeZoneReceiver = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the display supports fewer bits for each color in ambient mode. When true, we
|
|
||||||
* disable anti-aliasing in ambient mode.
|
|
||||||
*/
|
|
||||||
boolean mLowBitAmbient;
|
|
||||||
|
|
||||||
Bitmap mBackgroundBitmap;
|
|
||||||
Bitmap mBackgroundScaledBitmap;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(SurfaceHolder holder) {
|
public void onCreate(SurfaceHolder holder) {
|
||||||
@@ -93,32 +117,61 @@ public class SweepWatchFaceService extends CanvasWatchFaceService {
|
|||||||
.setShowSystemUiTime(false)
|
.setShowSystemUiTime(false)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
Resources resources = SweepWatchFaceService.this.getResources();
|
mBackgroundPaint = new Paint();
|
||||||
Drawable backgroundDrawable = resources.getDrawable(R.drawable.bg, null /* theme */);
|
mBackgroundPaint.setColor(Color.BLACK);
|
||||||
mBackgroundBitmap = ((BitmapDrawable) backgroundDrawable).getBitmap();
|
mBackgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bg);
|
||||||
|
|
||||||
|
/* Set defaults for colors */
|
||||||
|
mWatchHandColor = Color.WHITE;
|
||||||
|
mWatchHandHightlightColor = Color.RED;
|
||||||
|
mWatchHandShadowColor = Color.BLACK;
|
||||||
|
|
||||||
mHourPaint = new Paint();
|
mHourPaint = new Paint();
|
||||||
mHourPaint.setARGB(255, 200, 200, 200);
|
mHourPaint.setColor(mWatchHandColor);
|
||||||
mHourPaint.setStrokeWidth(5.f);
|
mHourPaint.setStrokeWidth(HOUR_STROKE_WIDTH);
|
||||||
mHourPaint.setAntiAlias(true);
|
mHourPaint.setAntiAlias(true);
|
||||||
mHourPaint.setStrokeCap(Paint.Cap.ROUND);
|
mHourPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
|
mHourPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
|
||||||
|
|
||||||
mMinutePaint = new Paint();
|
mMinutePaint = new Paint();
|
||||||
mMinutePaint.setARGB(255, 200, 200, 200);
|
mMinutePaint.setColor(mWatchHandColor);
|
||||||
mMinutePaint.setStrokeWidth(3.f);
|
mMinutePaint.setStrokeWidth(MINUTE_STROKE_WIDTH);
|
||||||
mMinutePaint.setAntiAlias(true);
|
mMinutePaint.setAntiAlias(true);
|
||||||
mMinutePaint.setStrokeCap(Paint.Cap.ROUND);
|
mMinutePaint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
|
mMinutePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
|
||||||
|
|
||||||
mSecondPaint = new Paint();
|
mSecondPaint = new Paint();
|
||||||
mSecondPaint.setARGB(255, 255, 0, 0);
|
mSecondPaint.setColor(mWatchHandHightlightColor);
|
||||||
mSecondPaint.setStrokeWidth(2.f);
|
mSecondPaint.setStrokeWidth(SECOND_TICK_STROKE_WIDTH);
|
||||||
mSecondPaint.setAntiAlias(true);
|
mSecondPaint.setAntiAlias(true);
|
||||||
mSecondPaint.setStrokeCap(Paint.Cap.ROUND);
|
mSecondPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
|
mSecondPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
|
||||||
|
|
||||||
mTickPaint = new Paint();
|
mTickAndCirclePaint = new Paint();
|
||||||
mTickPaint.setARGB(100, 255, 255, 255);
|
mTickAndCirclePaint.setColor(mWatchHandColor);
|
||||||
mTickPaint.setStrokeWidth(2.f);
|
mTickAndCirclePaint.setStrokeWidth(SECOND_TICK_STROKE_WIDTH);
|
||||||
mTickPaint.setAntiAlias(true);
|
mTickAndCirclePaint.setAntiAlias(true);
|
||||||
|
mTickAndCirclePaint.setStyle(Paint.Style.STROKE);
|
||||||
|
mTickAndCirclePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
|
||||||
|
|
||||||
|
/* Extract colors from background image to improve watchface style. */
|
||||||
|
Palette.generateAsync(
|
||||||
|
mBackgroundBitmap,
|
||||||
|
new Palette.PaletteAsyncListener() {
|
||||||
|
@Override
|
||||||
|
public void onGenerated(Palette palette) {
|
||||||
|
if (palette != null) {
|
||||||
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
|
Log.d(TAG, "Palette: " + palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
mWatchHandHightlightColor = palette.getVibrantColor(Color.RED);
|
||||||
|
mWatchHandColor = palette.getLightVibrantColor(Color.WHITE);
|
||||||
|
mWatchHandShadowColor = palette.getDarkMutedColor(Color.BLACK);
|
||||||
|
updateWatchHandStyle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
mCalendar = Calendar.getInstance();
|
mCalendar = Calendar.getInstance();
|
||||||
}
|
}
|
||||||
@@ -126,18 +179,17 @@ public class SweepWatchFaceService extends CanvasWatchFaceService {
|
|||||||
@Override
|
@Override
|
||||||
public void onPropertiesChanged(Bundle properties) {
|
public void onPropertiesChanged(Bundle properties) {
|
||||||
super.onPropertiesChanged(properties);
|
super.onPropertiesChanged(properties);
|
||||||
mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
|
|
||||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
Log.d(TAG, "onPropertiesChanged: low-bit ambient = " + mLowBitAmbient);
|
Log.d(TAG, "onPropertiesChanged: low-bit ambient = " + mLowBitAmbient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
|
||||||
|
mBurnInProtection = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTimeTick() {
|
public void onTimeTick() {
|
||||||
super.onTimeTick();
|
super.onTimeTick();
|
||||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
|
||||||
Log.d(TAG, "onTimeTick: ambient = " + isInAmbientMode());
|
|
||||||
}
|
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,22 +199,56 @@ public class SweepWatchFaceService extends CanvasWatchFaceService {
|
|||||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
Log.d(TAG, "onAmbientModeChanged: " + inAmbientMode);
|
Log.d(TAG, "onAmbientModeChanged: " + inAmbientMode);
|
||||||
}
|
}
|
||||||
if (mLowBitAmbient) {
|
mAmbient = inAmbientMode;
|
||||||
boolean antiAlias = !inAmbientMode;
|
|
||||||
mHourPaint.setAntiAlias(antiAlias);
|
updateWatchHandStyle();
|
||||||
mMinutePaint.setAntiAlias(antiAlias);
|
|
||||||
mSecondPaint.setAntiAlias(antiAlias);
|
|
||||||
mTickPaint.setAntiAlias(antiAlias);
|
|
||||||
}
|
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(mWatchHandHightlightColor);
|
||||||
|
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
|
@Override
|
||||||
public void onInterruptionFilterChanged(int interruptionFilter) {
|
public void onInterruptionFilterChanged(int interruptionFilter) {
|
||||||
super.onInterruptionFilterChanged(interruptionFilter);
|
super.onInterruptionFilterChanged(interruptionFilter);
|
||||||
boolean inMuteMode = (interruptionFilter == WatchFaceService.INTERRUPTION_FILTER_NONE);
|
boolean inMuteMode = (interruptionFilter == WatchFaceService.INTERRUPTION_FILTER_NONE);
|
||||||
if (mMute != inMuteMode) {
|
|
||||||
mMute = inMuteMode;
|
/* Dim display in mute mode. */
|
||||||
|
if (mMuteMode != inMuteMode) {
|
||||||
|
mMuteMode = inMuteMode;
|
||||||
mHourPaint.setAlpha(inMuteMode ? 100 : 255);
|
mHourPaint.setAlpha(inMuteMode ? 100 : 255);
|
||||||
mMinutePaint.setAlpha(inMuteMode ? 100 : 255);
|
mMinutePaint.setAlpha(inMuteMode ? 100 : 255);
|
||||||
mSecondPaint.setAlpha(inMuteMode ? 80 : 255);
|
mSecondPaint.setAlpha(inMuteMode ? 80 : 255);
|
||||||
@@ -172,13 +258,58 @@ public class SweepWatchFaceService extends CanvasWatchFaceService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||||
if (mBackgroundScaledBitmap == null
|
|
||||||
|| mBackgroundScaledBitmap.getWidth() != width
|
|
||||||
|| mBackgroundScaledBitmap.getHeight() != height) {
|
|
||||||
mBackgroundScaledBitmap = Bitmap.createScaledBitmap(mBackgroundBitmap,
|
|
||||||
width, height, true /* filter */);
|
|
||||||
}
|
|
||||||
super.onSurfaceChanged(holder, format, width, height);
|
super.onSurfaceChanged(holder, format, width, 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 = width / 2f;
|
||||||
|
mCenterY = height / 2f;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
@Override
|
||||||
@@ -189,59 +320,95 @@ public class SweepWatchFaceService extends CanvasWatchFaceService {
|
|||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
mCalendar.setTimeInMillis(now);
|
mCalendar.setTimeInMillis(now);
|
||||||
|
|
||||||
int width = bounds.width();
|
if (mAmbient && (mLowBitAmbient || mBurnInProtection)) {
|
||||||
int height = bounds.height();
|
canvas.drawColor(Color.BLACK);
|
||||||
|
} else if (mAmbient) {
|
||||||
|
canvas.drawBitmap(mGrayBackgroundBitmap, 0, 0, mBackgroundPaint);
|
||||||
|
} else {
|
||||||
|
canvas.drawBitmap(mBackgroundBitmap, 0, 0, mBackgroundPaint);
|
||||||
|
}
|
||||||
|
|
||||||
// Draw the background, scaled to fit.
|
/*
|
||||||
canvas.drawBitmap(mBackgroundScaledBitmap, 0, 0, null);
|
* 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
|
||||||
// Find the center. Ignore the window insets so that, on round watches with a
|
* creates them on top of the photo.
|
||||||
// "chin", the watch face is centered on the entire screen, not just the usable
|
*/
|
||||||
// portion.
|
float innerTickRadius = mCenterX - 10;
|
||||||
float centerX = width / 2f;
|
float outerTickRadius = mCenterX;
|
||||||
float centerY = height / 2f;
|
|
||||||
|
|
||||||
// Draw the ticks.
|
|
||||||
float innerTickRadius = centerX - 10;
|
|
||||||
float outerTickRadius = centerX;
|
|
||||||
for (int tickIndex = 0; tickIndex < 12; tickIndex++) {
|
for (int tickIndex = 0; tickIndex < 12; tickIndex++) {
|
||||||
float tickRot = (float) (tickIndex * Math.PI * 2 / 12);
|
float tickRot = (float) (tickIndex * Math.PI * 2 / 12);
|
||||||
float innerX = (float) Math.sin(tickRot) * innerTickRadius;
|
float innerX = (float) Math.sin(tickRot) * innerTickRadius;
|
||||||
float innerY = (float) -Math.cos(tickRot) * innerTickRadius;
|
float innerY = (float) -Math.cos(tickRot) * innerTickRadius;
|
||||||
float outerX = (float) Math.sin(tickRot) * outerTickRadius;
|
float outerX = (float) Math.sin(tickRot) * outerTickRadius;
|
||||||
float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
|
float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
|
||||||
canvas.drawLine(centerX + innerX, centerY + innerY,
|
canvas.drawLine(mCenterX + innerX, mCenterY + innerY,
|
||||||
centerX + outerX, centerY + outerY, mTickPaint);
|
mCenterX + outerX, mCenterY + outerY, mTickAndCirclePaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
float seconds =
|
/*
|
||||||
mCalendar.get(Calendar.SECOND) + mCalendar.get(Calendar.MILLISECOND) / 1000f;
|
* These calculations reflect the rotation in degrees per unit of time, e.g.,
|
||||||
float secRot = seconds / 60f * TWO_PI;
|
* 360 / 60 = 6 and 360 / 12 = 30.
|
||||||
float minutes = mCalendar.get(Calendar.MINUTE) + seconds / 60f;
|
*/
|
||||||
float minRot = minutes / 60f * TWO_PI;
|
final float seconds =
|
||||||
float hours = mCalendar.get(Calendar.HOUR) + minutes / 60f;
|
(mCalendar.get(Calendar.SECOND) + mCalendar.get(Calendar.MILLISECOND) / 1000f);
|
||||||
float hrRot = hours / 12f * TWO_PI;
|
final float secondsRotation = seconds * 6f;
|
||||||
|
|
||||||
float secLength = centerX - 20;
|
final float minutesRotation = mCalendar.get(Calendar.MINUTE) * 6f;
|
||||||
float minLength = centerX - 40;
|
|
||||||
float hrLength = centerX - 80;
|
|
||||||
|
|
||||||
if (!isInAmbientMode()) {
|
final float hourHandOffset = mCalendar.get(Calendar.MINUTE) / 2f;
|
||||||
float secX = (float) Math.sin(secRot) * secLength;
|
final float hoursRotation = (mCalendar.get(Calendar.HOUR) * 30) + hourHandOffset;
|
||||||
float secY = (float) -Math.cos(secRot) * secLength;
|
|
||||||
canvas.drawLine(centerX, centerY, centerX + secX, centerY + secY, mSecondPaint);
|
/*
|
||||||
|
* 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
float minX = (float) Math.sin(minRot) * minLength;
|
/* Draw every frame as long as we're visible and in interactive mode. */
|
||||||
float minY = (float) -Math.cos(minRot) * minLength;
|
if ((isVisible()) && (!mAmbient)) {
|
||||||
canvas.drawLine(centerX, centerY, centerX + minX, centerY + minY, mMinutePaint);
|
|
||||||
|
|
||||||
float hrX = (float) Math.sin(hrRot) * hrLength;
|
|
||||||
float hrY = (float) -Math.cos(hrRot) * hrLength;
|
|
||||||
canvas.drawLine(centerX, centerY, centerX + hrX, centerY + hrY, mHourPaint);
|
|
||||||
|
|
||||||
// Draw every frame as long as we're visible and in interactive mode.
|
|
||||||
if (isVisible() && !isInAmbientMode()) {
|
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,16 +419,20 @@ public class SweepWatchFaceService extends CanvasWatchFaceService {
|
|||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
registerReceiver();
|
registerReceiver();
|
||||||
|
/* Update time zone in case it changed while we weren't visible. */
|
||||||
// Update time zone in case it changed while we weren't visible.
|
|
||||||
mCalendar.setTimeZone(TimeZone.getDefault());
|
mCalendar.setTimeZone(TimeZone.getDefault());
|
||||||
|
|
||||||
invalidate();
|
invalidate();
|
||||||
} else {
|
} else {
|
||||||
unregisterReceiver();
|
unregisterReceiver();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPeekCardPositionUpdate(Rect rect) {
|
||||||
|
super.onPeekCardPositionUpdate(rect);
|
||||||
|
mPeekCardBounds.set(rect);
|
||||||
|
}
|
||||||
|
|
||||||
private void registerReceiver() {
|
private void registerReceiver() {
|
||||||
if (mRegisteredTimeZoneReceiver) {
|
if (mRegisteredTimeZoneReceiver) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ sample.group=Wearable
|
|||||||
|
|
||||||
This sample demonstrates how to create watch faces for android wear and includes a phone app
|
This sample demonstrates how to create watch faces for android wear and includes a phone app
|
||||||
and a wearable app. The wearable app has a variety of watch faces including analog, digital,
|
and a wearable app. The wearable app has a variety of watch faces including analog, digital,
|
||||||
opengl, calendar, etc. It also includes a watch-side configuration example. The phone app
|
opengl, calendar, interactive, etc. It also includes a watch-side configuration example.
|
||||||
includes a phone-side configuration example.
|
The phone app includes a phone-side configuration example.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
|||||||