Download File
Download Project
Settings
Line Wrap
Themes
default
ambiance
bespin
dracula
eclipse
material
mbo
mdn-like
neat
solarized dark
ttcn
zenburn
MyWatchFaceService.java
/* * 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.android.example.watchface; 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.Paint; import android.graphics.Rect; import android.os.Handler; import android.os.Message; import android.support.wearable.watchface.CanvasWatchFaceService; import android.support.wearable.watchface.WatchFaceStyle; import android.text.format.Time; import android.view.SurfaceHolder; import java.util.TimeZone; import java.util.concurrent.TimeUnit; /** * Analog watch face with a ticking 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. */ public class MyWatchFaceService extends CanvasWatchFaceService { /** * 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 { /* Handler to update the time once a second in interactive mode. */ private final Handler mUpdateTimeHandler = new Handler() { @Override public void handleMessage(Message message) { if (R.id.message_update == message.what) { invalidate(); if (shouldTimerBeRunning()) { long timeMs = System.currentTimeMillis(); long delayMs = INTERACTIVE_UPDATE_RATE_MS - (timeMs % INTERACTIVE_UPDATE_RATE_MS); mUpdateTimeHandler.sendEmptyMessageDelayed(R.id.message_update, delayMs); } } } }; private final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { mTime.clear(intent.getStringExtra("time-zone")); mTime.setToNow(); } }; private boolean mRegisteredTimeZoneReceiver = false; private static final float STROKE_WIDTH = 3f; private Time mTime; private Paint mBackgroundPaint; private Paint mHandPaint; private boolean mAmbient; private Bitmap mBackgroundBitmap; private float mHourHandLength; private float mMinuteHandLength; private float mSecondHandLength; private int mWidth; private int mHeight; private float mCenterX; private float mCenterY; private float mScale = 1; @Override public void onCreate(SurfaceHolder holder) { super.onCreate(holder); setWatchFaceStyle(new WatchFaceStyle.Builder(MyWatchFaceService.this) .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT) .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE) .setShowSystemUiTime(false) .build()); mBackgroundPaint = new Paint(); mBackgroundPaint.setColor(Color.BLACK); final int backgroundResId = R.drawable.custom_background; mBackgroundBitmap = BitmapFactory.decodeResource(getResources(), backgroundResId); mHandPaint = new Paint(); mHandPaint.setColor(Color.WHITE); mHandPaint.setStrokeWidth(STROKE_WIDTH); mHandPaint.setAntiAlias(true); mHandPaint.setStrokeCap(Paint.Cap.ROUND); mTime = new Time(); } @Override public void onDestroy() { mUpdateTimeHandler.removeMessages(R.id.message_update); super.onDestroy(); } @Override public void onTimeTick() { super.onTimeTick(); invalidate(); } @Override public void onAmbientModeChanged(boolean inAmbientMode) { super.onAmbientModeChanged(inAmbientMode); if (mAmbient != inAmbientMode) { mAmbient = inAmbientMode; invalidate(); } /* * Whether the timer should be running depends on whether we're visible (as well as * whether we're in ambient mode), so we may need to start or stop the timer. */ updateTimer(); } @Override public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { super.onSurfaceChanged(holder, format, width, height); mWidth = width; mHeight = height; /* * Find the coordinates of the center point on the screen. * 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; mScale = ((float) width) / (float) mBackgroundBitmap.getWidth(); /* * Calculate the lengths of the watch hands and store them in member variables. */ mHourHandLength = mCenterX - 80; mMinuteHandLength = mCenterX - 40; mSecondHandLength = mCenterX - 20; mBackgroundBitmap = Bitmap.createScaledBitmap(mBackgroundBitmap, (int) (mBackgroundBitmap.getWidth() * mScale), (int) (mBackgroundBitmap.getHeight() * mScale), true); } @Override public void onDraw(Canvas canvas, Rect bounds) { mTime.setToNow(); // Draw the background. canvas.drawBitmap(mBackgroundBitmap, 0, 0, mBackgroundPaint); /* * These calculations reflect the rotation in degrees per unit of * time, e.g. 360 / 60 = 6 and 360 / 12 = 30 */ final float secondsRotation = mTime.second * 6f; final float minutesRotation = mTime.minute * 6f; // account for the offset of the hour hand due to minutes of the hour. final float hourHandOffset = mTime.minute / 2f; final float hoursRotation = (mTime.hour * 30) + hourHandOffset; // save the canvas state before we begin to rotate it canvas.save(); canvas.rotate(hoursRotation, mCenterX, mCenterY); canvas.drawLine(mCenterX, mCenterY, mCenterX, mCenterY - mHourHandLength, mHandPaint); canvas.rotate(minutesRotation - hoursRotation, mCenterX, mCenterY); canvas.drawLine(mCenterX, mCenterY, mCenterX, mCenterY - mMinuteHandLength, mHandPaint); if (!mAmbient) { canvas.rotate(secondsRotation - minutesRotation, mCenterX, mCenterY); canvas.drawLine(mCenterX, mCenterY, mCenterX, mCenterY - mSecondHandLength, mHandPaint); } // restore the canvas' original orientation. canvas.restore(); } @Override public void onVisibilityChanged(boolean visible) { super.onVisibilityChanged(visible); if (visible) { registerReceiver(); // Update time zone in case it changed while we weren't visible. mTime.clear(TimeZone.getDefault().getID()); mTime.setToNow(); } else { unregisterReceiver(); } /* * Whether the timer should be running depends on whether we're visible * (as well as whether we're in ambient mode), * so we may need to start or stop the timer. */ updateTimer(); } private void registerReceiver() { if (mRegisteredTimeZoneReceiver) { return; } mRegisteredTimeZoneReceiver = true; IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED); MyWatchFaceService.this.registerReceiver(mTimeZoneReceiver, filter); } private void unregisterReceiver() { if (!mRegisteredTimeZoneReceiver) { return; } mRegisteredTimeZoneReceiver = false; MyWatchFaceService.this.unregisterReceiver(mTimeZoneReceiver); } private void updateTimer() { mUpdateTimeHandler.removeMessages(R.id.message_update); if (shouldTimerBeRunning()) { mUpdateTimeHandler.sendEmptyMessage(R.id.message_update); } } /** * Returns whether the {@link #mUpdateTimeHandler} timer should be running. The timer * should only run when we're visible and in interactive mode. */ private boolean shouldTimerBeRunning() { return isVisible() && !isInAmbientMode(); } } }