blob: 193f29a714ba7a00d13168df5ff8a3d7341b1345 [file] [log] [blame]
/*
* 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.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.wearable.watchface.CanvasWatchFaceService;
import android.support.wearable.watchface.WatchFaceService;
import android.support.wearable.watchface.WatchFaceStyle;
import android.util.Log;
import android.view.SurfaceHolder;
import java.util.Calendar;
import java.util.TimeZone;
/**
* Sample analog watch face with a sweep second hand. In ambient mode, the second hand isn't shown.
* On devices with low-bit ambient mode, the hands are drawn without anti-aliasing in ambient mode.
* The watch face is drawn with less contrast in mute mode.
*
* {@link AnalogWatchFaceService} is similar but has a ticking second hand.
*/
public class SweepWatchFaceService extends CanvasWatchFaceService {
private static final String TAG = "SweepWatchFaceService";
@Override
public Engine onCreateEngine() {
return new Engine();
}
private class Engine extends CanvasWatchFaceService.Engine {
static final float TWO_PI = (float) Math.PI * 2f;
Paint mHourPaint;
Paint mMinutePaint;
Paint mSecondPaint;
Paint mTickPaint;
boolean mMute;
Calendar mCalendar;
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
public void onCreate(SurfaceHolder holder) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onCreate");
}
super.onCreate(holder);
setWatchFaceStyle(new WatchFaceStyle.Builder(SweepWatchFaceService.this)
.setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
.setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
.setShowSystemUiTime(false)
.build());
Resources resources = SweepWatchFaceService.this.getResources();
Drawable backgroundDrawable = resources.getDrawable(R.drawable.bg, null /* theme */);
mBackgroundBitmap = ((BitmapDrawable) backgroundDrawable).getBitmap();
mHourPaint = new Paint();
mHourPaint.setARGB(255, 200, 200, 200);
mHourPaint.setStrokeWidth(5.f);
mHourPaint.setAntiAlias(true);
mHourPaint.setStrokeCap(Paint.Cap.ROUND);
mMinutePaint = new Paint();
mMinutePaint.setARGB(255, 200, 200, 200);
mMinutePaint.setStrokeWidth(3.f);
mMinutePaint.setAntiAlias(true);
mMinutePaint.setStrokeCap(Paint.Cap.ROUND);
mSecondPaint = new Paint();
mSecondPaint.setARGB(255, 255, 0, 0);
mSecondPaint.setStrokeWidth(2.f);
mSecondPaint.setAntiAlias(true);
mSecondPaint.setStrokeCap(Paint.Cap.ROUND);
mTickPaint = new Paint();
mTickPaint.setARGB(100, 255, 255, 255);
mTickPaint.setStrokeWidth(2.f);
mTickPaint.setAntiAlias(true);
mCalendar = Calendar.getInstance();
}
@Override
public void onPropertiesChanged(Bundle properties) {
super.onPropertiesChanged(properties);
mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onPropertiesChanged: low-bit ambient = " + mLowBitAmbient);
}
}
@Override
public void onTimeTick() {
super.onTimeTick();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onTimeTick: ambient = " + isInAmbientMode());
}
invalidate();
}
@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;
mHourPaint.setAntiAlias(antiAlias);
mMinutePaint.setAntiAlias(antiAlias);
mSecondPaint.setAntiAlias(antiAlias);
mTickPaint.setAntiAlias(antiAlias);
}
invalidate();
}
@Override
public void onInterruptionFilterChanged(int interruptionFilter) {
super.onInterruptionFilterChanged(interruptionFilter);
boolean inMuteMode = (interruptionFilter == WatchFaceService.INTERRUPTION_FILTER_NONE);
if (mMute != inMuteMode) {
mMute = inMuteMode;
mHourPaint.setAlpha(inMuteMode ? 100 : 255);
mMinutePaint.setAlpha(inMuteMode ? 100 : 255);
mSecondPaint.setAlpha(inMuteMode ? 80 : 255);
invalidate();
}
}
@Override
public void onDraw(Canvas canvas, Rect bounds) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "onDraw");
}
long now = System.currentTimeMillis();
mCalendar.setTimeInMillis(now);
int width = bounds.width();
int height = bounds.height();
// Draw the background, scaled to fit.
if (mBackgroundScaledBitmap == null
|| mBackgroundScaledBitmap.getWidth() != width
|| mBackgroundScaledBitmap.getHeight() != height) {
mBackgroundScaledBitmap = Bitmap.createScaledBitmap(mBackgroundBitmap,
width, height, true /* filter */);
}
canvas.drawBitmap(mBackgroundScaledBitmap, 0, 0, null);
// Find the center. 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.
float centerX = width / 2f;
float centerY = height / 2f;
// Draw the ticks.
float innerTickRadius = centerX - 10;
float outerTickRadius = centerX;
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(centerX + innerX, centerY + innerY,
centerX + outerX, centerY + outerY, mTickPaint);
}
float seconds =
mCalendar.get(Calendar.SECOND) + mCalendar.get(Calendar.MILLISECOND) / 1000f;
float secRot = seconds / 60f * TWO_PI;
float minutes = mCalendar.get(Calendar.MINUTE) + seconds / 60f;
float minRot = minutes / 60f * TWO_PI;
float hours = mCalendar.get(Calendar.HOUR) + minutes / 60f;
float hrRot = hours / 12f * TWO_PI;
float secLength = centerX - 20;
float minLength = centerX - 40;
float hrLength = centerX - 80;
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);
}
float minX = (float) Math.sin(minRot) * minLength;
float minY = (float) -Math.cos(minRot) * minLength;
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();
}
}
@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();
}
}
private void registerReceiver() {
if (mRegisteredTimeZoneReceiver) {
return;
}
mRegisteredTimeZoneReceiver = true;
IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
SweepWatchFaceService.this.registerReceiver(mTimeZoneReceiver, filter);
}
private void unregisterReceiver() {
if (!mRegisteredTimeZoneReceiver) {
return;
}
mRegisteredTimeZoneReceiver = false;
SweepWatchFaceService.this.unregisterReceiver(mTimeZoneReceiver);
}
}
}