16e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein/* 26e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * Copyright (C) 2013 The Android Open Source Project 36e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * 46e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * Licensed under the Apache License, Version 2.0 (the "License"); 56e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * you may not use this file except in compliance with the License. 66e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * You may obtain a copy of the License at 76e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * 86e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * http://www.apache.org/licenses/LICENSE-2.0 96e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * 106e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * Unless required by applicable law or agreed to in writing, software 116e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * distributed under the License is distributed on an "AS IS" BASIS, 126e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * See the License for the specific language governing permissions and 146e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * limitations under the License. 156e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein */ 166e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 17b8f95646fc0510eebfeaa27864023d630f34090fSam Blitzsteinpackage com.android.datetimepicker.time; 186e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 196e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.animation.Keyframe; 206e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.animation.ObjectAnimator; 216e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.animation.PropertyValuesHolder; 226e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.animation.ValueAnimator; 236e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.animation.ValueAnimator.AnimatorUpdateListener; 246e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.content.Context; 256e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.content.res.Resources; 266e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.graphics.Canvas; 276e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.graphics.Paint; 286e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.util.Log; 296e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.view.View; 306e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 316e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport com.android.datetimepicker.R; 321f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzsteinimport com.android.datetimepicker.Utils; 336e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 34f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein/** 35f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * View to show what number is selected. This will draw a blue circle over the number, with a blue 36f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * line coming from the center of the main circle to the edge of the blue selection. 37f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein */ 386e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinpublic class RadialSelectorView extends View { 396e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private static final String TAG = "RadialSelectorView"; 406e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 411f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein // Alpha level for selected circle. 421f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein private static final int SELECTED_ALPHA = Utils.SELECTED_ALPHA; 431f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein private static final int SELECTED_ALPHA_THEME_DARK = Utils.SELECTED_ALPHA_THEME_DARK; 441f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein // Alpha level for the line. 451f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein private static final int FULL_ALPHA = Utils.FULL_ALPHA; 461f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein 476e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private final Paint mPaint = new Paint(); 486e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 496e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private boolean mIsInitialized; 506e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private boolean mDrawValuesReady; 516e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 526e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mCircleRadiusMultiplier; 536e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mAmPmCircleRadiusMultiplier; 546e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mInnerNumbersRadiusMultiplier; 556e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mOuterNumbersRadiusMultiplier; 566e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mNumbersRadiusMultiplier; 576e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mSelectionRadiusMultiplier; 586e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mAnimationRadiusMultiplier; 596e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private boolean mIs24HourMode; 606e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private boolean mHasInnerCircle; 611f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein private int mSelectionAlpha; 626e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 636e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private int mXCenter; 646e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private int mYCenter; 656e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private int mCircleRadius; 666e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mTransitionMidRadiusMultiplier; 676e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mTransitionEndRadiusMultiplier; 686e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private int mLineLength; 696e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private int mSelectionRadius; 706e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private InvalidateUpdateListener mInvalidateUpdateListener; 716e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 726e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private int mSelectionDegrees; 736e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private double mSelectionRadians; 746e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private boolean mForceDrawDot; 756e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 766e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein public RadialSelectorView(Context context) { 776e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein super(context); 786e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mIsInitialized = false; 796e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 806e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 81f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein /** 82f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * Initialize this selector with the state of the picker. 83f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * @param context Current context. 84f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * @param is24HourMode Whether the selector is in 24-hour mode, which will tell us 85f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * whether the circle's center is moved up slightly to make room for the AM/PM circles. 86f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * @param hasInnerCircle Whether we have both an inner and an outer circle of numbers 87f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * that may be selected. Should be true for 24-hour mode in the hours circle. 88f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * @param disappearsOut Whether the numbers' animation will have them disappearing out 89f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * or disappearing in. 90f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * @param selectionDegrees The initial degrees to be selected. 91f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * @param isInnerCircle Whether the initial selection is in the inner or outer circle. 92f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * Will be ignored when hasInnerCircle is false. 93f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein */ 94b8f95646fc0510eebfeaa27864023d630f34090fSam Blitzstein public void initialize(Context context, boolean is24HourMode, boolean hasInnerCircle, 95b8f95646fc0510eebfeaa27864023d630f34090fSam Blitzstein boolean disappearsOut, int selectionDegrees, boolean isInnerCircle) { 966e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (mIsInitialized) { 976e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Log.e(TAG, "This RadialSelectorView may only be initialized once."); 986e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return; 996e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 1006e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 1016e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Resources res = context.getResources(); 1026e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 1036e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int blue = res.getColor(R.color.blue); 1046e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mPaint.setColor(blue); 1056e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mPaint.setAntiAlias(true); 1061f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein mSelectionAlpha = SELECTED_ALPHA; 1076e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 108f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // Calculate values for the circle radius size. 1096e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mIs24HourMode = is24HourMode; 1106e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (is24HourMode) { 1116e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mCircleRadiusMultiplier = Float.parseFloat( 1126e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein res.getString(R.string.circle_radius_multiplier_24HourMode)); 1136e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } else { 1146e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mCircleRadiusMultiplier = Float.parseFloat( 1156e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein res.getString(R.string.circle_radius_multiplier)); 1166e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mAmPmCircleRadiusMultiplier = 1176e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Float.parseFloat(res.getString(R.string.ampm_circle_radius_multiplier)); 1186e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 1196e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 120f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // Calculate values for the radius size(s) of the numbers circle(s). 1216e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mHasInnerCircle = hasInnerCircle; 1226e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (hasInnerCircle) { 1236e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mInnerNumbersRadiusMultiplier = 1246e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Float.parseFloat(res.getString(R.string.numbers_radius_multiplier_inner)); 1256e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mOuterNumbersRadiusMultiplier = 1266e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Float.parseFloat(res.getString(R.string.numbers_radius_multiplier_outer)); 1276e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } else { 1286e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mNumbersRadiusMultiplier = 1296e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Float.parseFloat(res.getString(R.string.numbers_radius_multiplier_normal)); 1306e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 1316e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mSelectionRadiusMultiplier = 1326e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Float.parseFloat(res.getString(R.string.selection_radius_multiplier)); 1336e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 134f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // Calculate values for the transition mid-way states. 1356e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mAnimationRadiusMultiplier = 1; 1366e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mTransitionMidRadiusMultiplier = 1f + (0.05f * (disappearsOut? -1 : 1)); 1376e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mTransitionEndRadiusMultiplier = 1f + (0.3f * (disappearsOut? 1 : -1)); 1386e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mInvalidateUpdateListener = new InvalidateUpdateListener(); 1396e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 140f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein setSelection(selectionDegrees, isInnerCircle, false); 1416e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mIsInitialized = true; 1426e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 1436e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 1441f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein /* package */ void setTheme(Context context, boolean themeDark) { 1451f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein Resources res = context.getResources(); 1461f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein int color; 1471f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein if (themeDark) { 1481f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein color = res.getColor(R.color.red); 1491f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein mSelectionAlpha = SELECTED_ALPHA_THEME_DARK; 1501f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein } else { 1511f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein color = res.getColor(R.color.blue); 1521f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein mSelectionAlpha = SELECTED_ALPHA; 1531f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein } 1541f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein mPaint.setColor(color); 1551f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein } 1561f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein 157f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein /** 158f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * Set the selection. 159f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * @param selectionDegrees The degrees to be selected. 160f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * @param isInnerCircle Whether the selection should be in the inner circle or outer. Will be 161f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * ignored if hasInnerCircle was initialized to false. 162f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * @param forceDrawDot Whether to force the dot in the center of the selection circle to be 163f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * drawn. If false, the dot will be drawn only when the degrees is not a multiple of 30, i.e. 164f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * the selection is not on a visible number. 165f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein */ 166f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein public void setSelection(int selectionDegrees, boolean isInnerCircle, boolean forceDrawDot) { 1676e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mSelectionDegrees = selectionDegrees; 1686e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mSelectionRadians = selectionDegrees * Math.PI / 180; 1696e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mForceDrawDot = forceDrawDot; 1706e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 1716e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (mHasInnerCircle) { 1726e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (isInnerCircle) { 1736e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mNumbersRadiusMultiplier = mInnerNumbersRadiusMultiplier; 1746e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } else { 1756e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mNumbersRadiusMultiplier = mOuterNumbersRadiusMultiplier; 1766e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 1776e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 1786e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 1796e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 180f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein /** 181f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * Allows for smoother animations. 182f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein */ 1833d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein @Override 1843d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein public boolean hasOverlappingRendering() { 1853d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein return false; 1863d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein } 1873d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein 188f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein /** 189f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * Set the multiplier for the radius. Will be used during animations to move in/out. 190f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein */ 1916e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein public void setAnimationRadiusMultiplier(float animationRadiusMultiplier) { 1926e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mAnimationRadiusMultiplier = animationRadiusMultiplier; 1936e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 1946e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 1956e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein public int getDegreesFromCoords(float pointX, float pointY, boolean forceLegal, 1966e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein final Boolean[] isInnerCircle) { 1976e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (!mDrawValuesReady) { 1986e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return -1; 1996e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2006e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 2016e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein double hypotenuse = Math.sqrt( 2026e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein (pointY - mYCenter)*(pointY - mYCenter) + 2036e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein (pointX - mXCenter)*(pointX - mXCenter)); 204f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // Check if we're outside the range 2056e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (mHasInnerCircle) { 2066e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (forceLegal) { 2076e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // If we're told to force the coordinates to be legal, we'll set the isInnerCircle 2086e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // boolean based based off whichever number the coordinates are closer to. 2096e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int innerNumberRadius = (int) (mCircleRadius * mInnerNumbersRadiusMultiplier); 2106e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int distanceToInnerNumber = (int) Math.abs(hypotenuse - innerNumberRadius); 2116e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int outerNumberRadius = (int) (mCircleRadius * mOuterNumbersRadiusMultiplier); 2126e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int distanceToOuterNumber = (int) Math.abs(hypotenuse - outerNumberRadius); 2136e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 2146e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein isInnerCircle[0] = (distanceToInnerNumber <= distanceToOuterNumber); 2156e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } else { 2166e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // Otherwise, if we're close enough to either number (with the space between the 2176e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // two allotted equally), set the isInnerCircle boolean as the closer one. 2186e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // appropriately, but otherwise return -1. 2196e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int minAllowedHypotenuseForInnerNumber = 2206e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein (int) (mCircleRadius * mInnerNumbersRadiusMultiplier) - mSelectionRadius; 2216e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int maxAllowedHypotenuseForOuterNumber = 2226e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein (int) (mCircleRadius * mOuterNumbersRadiusMultiplier) + mSelectionRadius; 2236e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int halfwayHypotenusePoint = (int) (mCircleRadius * 2246e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein ((mOuterNumbersRadiusMultiplier + mInnerNumbersRadiusMultiplier) / 2)); 2256e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 2266e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (hypotenuse >= minAllowedHypotenuseForInnerNumber && 2276e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein hypotenuse <= halfwayHypotenusePoint) { 2286e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein isInnerCircle[0] = true; 2296e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } else if (hypotenuse <= maxAllowedHypotenuseForOuterNumber && 2306e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein hypotenuse >= halfwayHypotenusePoint) { 2316e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein isInnerCircle[0] = false; 2326e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } else { 2336e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return -1; 2346e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2356e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2366e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } else { 2376e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // If there's just one circle, we'll need to return -1 if: 2386e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // we're not told to force the coordinates to be legal, and 2396e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // the coordinates' distance to the number is within the allowed distance. 2406e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (!forceLegal) { 2416e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int distanceToNumber = (int) Math.abs(hypotenuse - mLineLength); 2426e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // The max allowed distance will be defined as the distance from the center of the 2436e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // number to the edge of the circle. 2446e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int maxAllowedDistance = (int) (mCircleRadius * (1 - mNumbersRadiusMultiplier)); 2456e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (distanceToNumber > maxAllowedDistance) { 2466e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return -1; 2476e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2486e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2496e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2506e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 2516e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 2526e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float opposite = Math.abs(pointY - mYCenter); 2536e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein double radians = Math.asin(opposite / hypotenuse); 2546e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int degrees = (int) (radians * 180 / Math.PI); 2556e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 2566e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // Now we have to translate to the correct quadrant. 2576e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein boolean rightSide = (pointX > mXCenter); 2586e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein boolean topSide = (pointY < mYCenter); 2596e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (rightSide && topSide) { 2606e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein degrees = 90 - degrees; 2616e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } else if (rightSide && !topSide) { 2626e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein degrees = 90 + degrees; 2636e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } else if (!rightSide && !topSide) { 2646e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein degrees = 270 - degrees; 2656e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } else if (!rightSide && topSide) { 2666e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein degrees = 270 + degrees; 2676e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2686e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return degrees; 2696e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2706e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 2716e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein @Override 2726e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein public void onDraw(Canvas canvas) { 2736e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int viewWidth = getWidth(); 2746e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (viewWidth == 0 || !mIsInitialized) { 2756e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return; 2766e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2776e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 2786e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (!mDrawValuesReady) { 2796e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mXCenter = getWidth() / 2; 2806e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mYCenter = getHeight() / 2; 2816e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mCircleRadius = (int) (Math.min(mXCenter, mYCenter) * mCircleRadiusMultiplier); 2826e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 2836e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (!mIs24HourMode) { 2846e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // We'll need to draw the AM/PM circles, so the main circle will need to have 2856e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // a slightly higher center. To keep the entire view centered vertically, we'll 2866e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // have to push it up by half the radius of the AM/PM circles. 2876e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int amPmCircleRadius = (int) (mCircleRadius * mAmPmCircleRadiusMultiplier); 2886e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mYCenter -= amPmCircleRadius / 2; 2896e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2906e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 2916e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mSelectionRadius = (int) (mCircleRadius * mSelectionRadiusMultiplier); 2926e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 2936e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mDrawValuesReady = true; 2946e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2956e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 296f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // Calculate the current radius at which to place the selection circle. 2976e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mLineLength = (int) (mCircleRadius * mNumbersRadiusMultiplier * mAnimationRadiusMultiplier); 2986e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int pointX = mXCenter + (int) (mLineLength * Math.sin(mSelectionRadians)); 2996e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int pointY = mYCenter - (int) (mLineLength * Math.cos(mSelectionRadians)); 3006e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 301f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // Draw the selection circle. 3021f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein mPaint.setAlpha(mSelectionAlpha); 3036e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein canvas.drawCircle(pointX, pointY, mSelectionRadius, mPaint); 3046e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3056e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (mForceDrawDot | mSelectionDegrees % 30 != 0) { 306f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // We're not on a direct tick (or we've been told to draw the dot anyway). 3071f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein mPaint.setAlpha(FULL_ALPHA); 3083d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein canvas.drawCircle(pointX, pointY, (mSelectionRadius * 2 / 7), mPaint); 3096e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } else { 310f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // We're not drawing the dot, so shorten the line to only go as far as the edge of the 311f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // selection circle. 3126e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int lineLength = mLineLength; 3136e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein lineLength -= mSelectionRadius; 3146e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein pointX = mXCenter + (int) (lineLength * Math.sin(mSelectionRadians)); 3156e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein pointY = mYCenter - (int) (lineLength * Math.cos(mSelectionRadians)); 3166e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 3176e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 318f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // Draw the line from the center of the circle. 319f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein mPaint.setAlpha(255); 320f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein mPaint.setStrokeWidth(1); 321f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein canvas.drawLine(mXCenter, mYCenter, pointX, pointY, mPaint); 3226e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 3236e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3246e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein public ObjectAnimator getDisappearAnimator() { 3256e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (!mIsInitialized || !mDrawValuesReady) { 3266e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Log.e(TAG, "RadialSelectorView was not ready for animation."); 3276e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return null; 3286e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 3296e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3306e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Keyframe kf0, kf1, kf2; 3316e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float midwayPoint = 0.2f; 3326e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int duration = 500; 3336e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3346e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf0 = Keyframe.ofFloat(0f, 1); 3356e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf1 = Keyframe.ofFloat(midwayPoint, mTransitionMidRadiusMultiplier); 3366e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf2 = Keyframe.ofFloat(1f, mTransitionEndRadiusMultiplier); 3376e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein PropertyValuesHolder radiusDisappear = PropertyValuesHolder.ofKeyframe( 3386e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein "animationRadiusMultiplier", kf0, kf1, kf2); 3396e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3406e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf0 = Keyframe.ofFloat(0f, 1f); 3416e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf1 = Keyframe.ofFloat(1f, 0f); 3426e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein PropertyValuesHolder fadeOut = PropertyValuesHolder.ofKeyframe("alpha", kf0, kf1); 3436e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3446e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein ObjectAnimator disappearAnimator = ObjectAnimator.ofPropertyValuesHolder( 3456e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein this, radiusDisappear, fadeOut).setDuration(duration); 3466e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein disappearAnimator.addUpdateListener(mInvalidateUpdateListener); 3476e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3486e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return disappearAnimator; 3496e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 3506e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3516e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein public ObjectAnimator getReappearAnimator() { 3526e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (!mIsInitialized || !mDrawValuesReady) { 3536e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Log.e(TAG, "RadialSelectorView was not ready for animation."); 3546e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return null; 3556e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 3566e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3576e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Keyframe kf0, kf1, kf2, kf3; 3586e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float midwayPoint = 0.2f; 3596e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int duration = 500; 3606e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3616e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // The time points are half of what they would normally be, because this animation is 3626e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // staggered against the disappear so they happen seamlessly. The reappear starts 3636e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // halfway into the disappear. 364b8f95646fc0510eebfeaa27864023d630f34090fSam Blitzstein float delayMultiplier = 0.25f; 365b8f95646fc0510eebfeaa27864023d630f34090fSam Blitzstein float transitionDurationMultiplier = 1f; 3666e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float totalDurationMultiplier = transitionDurationMultiplier + delayMultiplier; 3676e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int totalDuration = (int) (duration * totalDurationMultiplier); 3686e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float delayPoint = (delayMultiplier * duration) / totalDuration; 3696e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein midwayPoint = 1 - (midwayPoint * (1 - delayPoint)); 3706e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3716e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf0 = Keyframe.ofFloat(0f, mTransitionEndRadiusMultiplier); 3726e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf1 = Keyframe.ofFloat(delayPoint, mTransitionEndRadiusMultiplier); 3736e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf2 = Keyframe.ofFloat(midwayPoint, mTransitionMidRadiusMultiplier); 3746e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf3 = Keyframe.ofFloat(1f, 1); 3756e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein PropertyValuesHolder radiusReappear = PropertyValuesHolder.ofKeyframe( 3766e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein "animationRadiusMultiplier", kf0, kf1, kf2, kf3); 3776e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3786e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf0 = Keyframe.ofFloat(0f, 0f); 3796e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf1 = Keyframe.ofFloat(delayPoint, 0f); 3806e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf2 = Keyframe.ofFloat(1f, 1f); 3816e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein PropertyValuesHolder fadeIn = PropertyValuesHolder.ofKeyframe("alpha", kf0, kf1, kf2); 3826e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3836e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein ObjectAnimator reappearAnimator = ObjectAnimator.ofPropertyValuesHolder( 3846e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein this, radiusReappear, fadeIn).setDuration(totalDuration); 3856e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein reappearAnimator.addUpdateListener(mInvalidateUpdateListener); 3866e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return reappearAnimator; 3876e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 3886e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 389f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein /** 390f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * We'll need to invalidate during the animation. 391f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein */ 3926e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private class InvalidateUpdateListener implements AnimatorUpdateListener { 3936e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein @Override 3946e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein public void onAnimationUpdate(ValueAnimator animation) { 3956e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein RadialSelectorView.this.invalidate(); 3966e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 3976e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 3986e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein} 399