/* * Copyright (C) 2013 Fairphone 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 org.fairphone.launcher.edgeswipe.ui; import org.fairphone.launcher.R; import org.fairphone.launcher.util.KWMathUtils; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; public class CircularCountdownTimerView extends View { public interface CircularCountdownListener { public void onCountdownFinished(CircularCountdownTimerView countdownView); } private Drawable timerDrawable; private RectF clipBounds = new RectF(); private RectF viewBounds = new RectF(); private Rect viewBoundsI = new Rect(); private Paint clipPaint; private Paint sourcePaint; private Bitmap viewBitmap; private BitmapShader timerShader; private float curFillRatio = 1.0f; private float startFillRatio = 1.0f; private float endFillRatio = 0.0f; private long animationDuration = 0; private long animationDelay = 0; private long curAnimationTime = 0; private long prevFrame = 0; private boolean isAnimating = false; private int prevWidth = 0; private int prevHeight = 0; CircularCountdownListener listener; public CircularCountdownTimerView(Context context) { super(context); init(); } public CircularCountdownTimerView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CircularCountdownTimerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public void startCountdownAnimation(long duration) { startCountdownAnimation(duration, 0); } public void startCountdownAnimation(long duration, long delay) { prevFrame = System.currentTimeMillis(); animationDelay = delay; startFillRatio = 1.0f; curFillRatio = 1.0f; endFillRatio = 0.0f; isAnimating = true; curAnimationTime = 0; animationDuration = duration; postInvalidate(); } public boolean isCountingDown() { return isAnimating; } public boolean isFinished() { return curFillRatio == 0.0f; } public float getCountdownRatio() { return curFillRatio; } public void pauseCountdownAnimation() { isAnimating = false; postInvalidate(); } public void resumeCountdownAnimation() { isAnimating = true; prevFrame = System.currentTimeMillis(); postInvalidate(); } public void cancelCountdownAnimation() { alpha = 0; isAnimating = false; curFillRatio = 1.0f; curAnimationTime = 0; postInvalidate(); } public void finishCountdownAnimation() { alpha = 0; postInvalidate(); curAnimationTime = animationDuration; curFillRatio = 0.0f; } public void forceCountdownBegin() { alpha = 0; curFillRatio = 1.0f; isAnimating = false; postInvalidate(); } public void forceCountdownEnd() { alpha = 0; curFillRatio = 0.0f; isAnimating = false; postInvalidate(); } public CircularCountdownListener getCircularCountdownListener() { return listener; } public void setCircularCountdownListener(CircularCountdownListener listener) { this.listener = listener; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); } @Override protected void onDetachedFromWindow() { clipPaint.setShader(null); timerShader = null; viewBitmap.recycle(); viewBitmap = null; super.onDetachedFromWindow(); } private void init() { timerDrawable = getContext().getResources().getDrawable( R.drawable.fp_fav_icon_ring); clipPaint = new Paint(); clipPaint.setColor(0xFFFFFFFF); sourcePaint = new Paint(); sourcePaint.setColor(0xFFFFFFFF); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); float w = getWidth(); float h = getHeight(); float w2 = w / 2; float h2 = h / 2; timerDrawable.setBounds(0, 0, (int) w, (int) h); float radius = (float) Math.sqrt(w2 * w2 + h2 * h2); clipBounds.set(w2 - radius, h2 - radius, w2 + radius, h2 + radius); viewBounds.set(0, 0, (int) w, (int) h); viewBoundsI.set(0, 0, (int) w, (int) h); if (getWidth() != prevWidth || getHeight() != prevHeight || viewBitmap == null) { if (viewBitmap != null) { viewBitmap.recycle(); } viewBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(viewBitmap); // canvas.drawBitmap(sourceBitmap, auxRectI, viewBoundsI, // sourcePaint); timerDrawable.setBounds(viewBoundsI); timerDrawable.draw(canvas); timerShader = new BitmapShader(viewBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); clipPaint.setShader(timerShader); } prevWidth = getWidth(); prevHeight = getHeight(); } @Override public void draw(Canvas canvas) { super.draw(canvas); updateAnimation(); if (curAnimationTime - animationDelay > 0) { alpha += 0.03f; if(alpha > 1.0f){ alpha = 1.0f; } } setAlpha(alpha); if (timerShader != null) { canvas.drawArc(viewBounds, 0, 360.f * curFillRatio, true, clipPaint); } } float alpha = 0; private void updateAnimation() { long curTime = System.currentTimeMillis(); long dt = curTime - prevFrame; if (dt > 500) { dt = 1000 / 60; } if (isAnimating) { curAnimationTime += dt; boolean isFinished = false; long curAnimTime = Math.max(curAnimationTime - animationDelay, 0); if (curAnimTime >= animationDuration) { curAnimTime = animationDuration; curAnimTime = animationDuration + animationDelay; isFinished = true; isAnimating = false; alpha = 0; } float timeRatio = KWMathUtils.getLongRatio(0, animationDuration, curAnimTime); curFillRatio = KWMathUtils.blend(startFillRatio, endFillRatio, timeRatio); invalidate(); if (isFinished) { alpha = 0; if (listener != null) { listener.onCountdownFinished(this); } } } prevFrame = curTime; } }