/* * Copyright (C) 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.google.android.glass.sample.stopwatch; import com.google.android.glass.timeline.DirectRenderingCallback; import android.content.Context; import android.graphics.Canvas; import android.os.SystemClock; import android.util.Log; import android.view.SurfaceHolder; import android.view.View; /** * {@link DirectRenderingCallback} used to draw the chronometer on the timeline {@link LiveCard}. * Rendering requires that: * <ol> * <li>a {@link SurfaceHolder} has been created through monitoring the * {@link SurfaceHolder.Callback#onSurfaceCreated(SurfaceHolder)} and * {@link SurfaceHolder.Callback#onSurfaceDestroyed(SurfaceHolder)} callbacks. * <li>rendering has not been paused (defaults to rendering) through monitoring the * {@link DirecRenderingCallback#renderingPaused(SurfaceHolder, boolean)} callback. * </ol> * As this class uses an inflated {@link View} to draw on the {@link SurfaceHolder}'s * {@link Canvas}, monitoring the * {@link SurfaceHolder.Callback#onSurfaceChanged(SurfaceHolder, int, int, int)} callback is also * required to properly measure and layout the {@link View}'s dimension. */ public class ChronometerDrawer implements DirectRenderingCallback { private static final String TAG = ChronometerDrawer.class.getSimpleName(); private static final int COUNT_DOWN_VALUE = 3; private final CountDownView mCountDownView; private final ChronometerView mChronometerView; private SurfaceHolder mHolder; private boolean mCountDownDone; private boolean mRenderingPaused; private final CountDownView.Listener mCountDownListener = new CountDownView.Listener() { @Override public void onTick(long millisUntilFinish) { if (mHolder != null) { draw(mCountDownView); } } @Override public void onFinish() { mCountDownDone = true; mChronometerView.setBaseMillis(SystemClock.elapsedRealtime()); updateRenderingState(); } }; private final ChronometerView.Listener mChronometerListener = new ChronometerView.Listener() { @Override public void onChange() { if (mHolder != null) { draw(mChronometerView); } } }; public ChronometerDrawer(Context context) { this(new CountDownView(context), new ChronometerView(context)); } public ChronometerDrawer(CountDownView countDownView, ChronometerView chronometerView) { mCountDownView = countDownView; mCountDownView.setCountDown(COUNT_DOWN_VALUE); mCountDownView.setListener(mCountDownListener); mChronometerView = chronometerView; mChronometerView.setListener(mChronometerListener); } /** * Uses the provided {@code width} and {@code height} to measure and layout the inflated * {@link CountDownView} and {@link ChronometerView}. */ @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // Measure and layout the view with the canvas dimensions. int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY); int measuredHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); mCountDownView.measure(measuredWidth, measuredHeight); mCountDownView.layout( 0, 0, mCountDownView.getMeasuredWidth(), mCountDownView.getMeasuredHeight()); mChronometerView.measure(measuredWidth, measuredHeight); mChronometerView.layout( 0, 0, mChronometerView.getMeasuredWidth(), mChronometerView.getMeasuredHeight()); } /** * Keeps the created {@link SurfaceHolder} and updates this class' rendering state. */ @Override public void surfaceCreated(SurfaceHolder holder) { // The creation of a new Surface implicitly resumes the rendering. mRenderingPaused = false; mHolder = holder; updateRenderingState(); } /** * Removes the {@link SurfaceHolder} used for drawing and stops rendering. */ @Override public void surfaceDestroyed(SurfaceHolder holder) { mHolder = null; updateRenderingState(); } /** * Updates this class' rendering state according to the provided {@code paused} flag. */ @Override public void renderingPaused(SurfaceHolder holder, boolean paused) { mRenderingPaused = paused; updateRenderingState(); } /** * Starts or stops rendering according to the {@link LiveCard}'s state. */ private void updateRenderingState() { if (mHolder != null && !mRenderingPaused) { if (mCountDownDone) { mChronometerView.start(); } else { mCountDownView.start(); } } else { mChronometerView.stop(); } } /** * Draws the view in the SurfaceHolder's canvas. */ private void draw(View view) { Canvas canvas; try { canvas = mHolder.lockCanvas(); } catch (Exception e) { Log.e(TAG, "Unable to lock canvas: " + e); return; } if (canvas != null) { view.draw(canvas); mHolder.unlockCanvasAndPost(canvas); } } }