/* * Copyright (C) 2020 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.emergency.widgets.countdown; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Typeface; import androidx.annotation.GuardedBy; import com.android.emergency.R; import java.time.Duration; /** * The renderer which renders the text view to show how many seconds left to count down. */ public class CountDownRenderer { @GuardedBy("this") private final Paint mTimerTextPaint; @GuardedBy("this") private final Paint mUnitTextPaint; @GuardedBy("this") private RectF mBounds = null; @GuardedBy("this") private boolean mIsRevealed = false; @GuardedBy("this") private Duration mCountDownLeft; private Context mContext; private String mSecondUnitText; private float mUnitTextLeftMargin; CountDownRenderer(Context context) { mContext = context; mTimerTextPaint = new Paint(); mTimerTextPaint.setTextAlign(Paint.Align.CENTER); mTimerTextPaint.setTypeface(Typeface.SANS_SERIF); mTimerTextPaint.setColor(Color.WHITE); mTimerTextPaint.setAntiAlias(true); mUnitTextPaint = new Paint(); mUnitTextPaint.setTypeface(Typeface.SANS_SERIF); mUnitTextPaint.setTextAlign(Paint.Align.CENTER); mUnitTextPaint.setColor(Color.WHITE); mUnitTextPaint.setAntiAlias(true); mSecondUnitText = context.getString(R.string.count_down_unit); } /** Shows the count down rendering. */ synchronized void show() { mIsRevealed = true; } /** Returns true if count down text should be displayed, false otherwise. */ synchronized boolean isRevealed() { return mIsRevealed; } /** * Draws the count down text. * *

Called from {@link LoopingAnimationThread}. */ synchronized void draw(Canvas canvas) { if (!mIsRevealed || mBounds == null || mCountDownLeft == null) { return; } String timerText = Long.toString(mCountDownLeft.getSeconds()); RectF timerTextBounds = measureText(timerText, mTimerTextPaint); RectF unitTextBounds = measureText(mSecondUnitText, mUnitTextPaint); // Shift timer text and unit text to share baseline. unitTextBounds.offset(0, timerTextBounds.height() - unitTextBounds.height()); // Calculate final text bound that all text will be drawn inside of. RectF finalTextBounds = new RectF( 0, 0, timerTextBounds.width() + unitTextBounds.width() + mUnitTextLeftMargin, timerTextBounds.height()); // Offset unit text bounds to right of timer text bounds. unitTextBounds.offset(timerTextBounds.width() + mUnitTextLeftMargin, 0); // Center final text bounds in provided bounds and place timer text and unit text inside. finalTextBounds.offset( mBounds.left + (mBounds.width() - finalTextBounds.width()) * 0.5f, mBounds.bottom - (mBounds.height() - finalTextBounds.height())); timerTextBounds.offset(finalTextBounds.left, finalTextBounds.top); unitTextBounds.offset(finalTextBounds.left, finalTextBounds.top); // Draw text. canvas.drawText( timerText, timerTextBounds.centerX(), timerTextBounds.bottom, mTimerTextPaint); canvas.drawText( mSecondUnitText, unitTextBounds.centerX(), unitTextBounds.bottom, mUnitTextPaint); } /** * Sets the bounds text should be drawn in. * *

Called from ui thread. */ synchronized void updateBounds(RectF loopBounds, float totalDiameter) { this.mBounds = new RectF(loopBounds.left, loopBounds.top, loopBounds.right, loopBounds.bottom); float textSizeToBoundsRatio = mContext.getResources().getFloat( R.dimen.count_down_view_text_size_to_diameter_ratio); float unitTextToTimerTextSizeRatio = mContext.getResources().getFloat( R.dimen.count_down_view_unit_text_to_time_text_size_ratio); float unitTextLeftMarginToBoundsRatio = mContext.getResources().getFloat( R.dimen.count_down_view_unit_text_left_margin_to_diameter_ratio); mTimerTextPaint.setTextSize(textSizeToBoundsRatio * totalDiameter); mUnitTextPaint.setTextSize( textSizeToBoundsRatio * unitTextToTimerTextSizeRatio * totalDiameter); mUnitTextLeftMargin = unitTextLeftMarginToBoundsRatio * totalDiameter; } private static RectF measureText(String text, Paint textPaint) { Rect textBounds = new Rect(); textPaint.getTextBounds(text, 0, text.length(), textBounds); RectF textBoundsF = new RectF(textBounds); // Readjust text to be aligned to origin as their top left. textBoundsF.offset(-textBounds.left, -textBounds.top); return textBoundsF; } synchronized void setCountDownLeft(Duration timeLeft) { mCountDownLeft = timeLeft; } }