RadialTimePickerView.java revision ba9bf41a77261471a4dc9d7964aec41726b4e4e6
1eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio/*
2eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * Copyright (C) 2013 The Android Open Source Project
3eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio *
4eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * Licensed under the Apache License, Version 2.0 (the "License");
5eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * you may not use this file except in compliance with the License.
6eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * You may obtain a copy of the License at
7eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio *
8eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio *      http://www.apache.org/licenses/LICENSE-2.0
9eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio *
10eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * Unless required by applicable law or agreed to in writing, software
11eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * distributed under the License is distributed on an "AS IS" BASIS,
12eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * See the License for the specific language governing permissions and
14eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * limitations under the License.
15eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio */
16eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
17eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Megliopackage android.widget;
18eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
19eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.animation.Animator;
20eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.animation.AnimatorSet;
21eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.animation.Keyframe;
22eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.animation.ObjectAnimator;
23eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.animation.PropertyValuesHolder;
24eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.animation.ValueAnimator;
25eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.annotation.SuppressLint;
26eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.content.Context;
2760727e07c6ef72e2f494266939c02494a3df28f8Alan Viveretteimport android.content.res.ColorStateList;
28eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.content.res.Resources;
29eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.content.res.TypedArray;
30eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.graphics.Canvas;
31eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.graphics.Color;
32eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.graphics.Paint;
33eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.graphics.Typeface;
34eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.graphics.RectF;
35eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.os.Bundle;
36eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.text.format.DateUtils;
37eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.text.format.Time;
38eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.util.AttributeSet;
39eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.util.Log;
405134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viveretteimport android.util.TypedValue;
41eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.HapticFeedbackConstants;
42eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.MotionEvent;
43eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.View;
44eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.ViewGroup;
45eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.accessibility.AccessibilityEvent;
46eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.accessibility.AccessibilityNodeInfo;
47eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette
48eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport com.android.internal.R;
49eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
50eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport java.text.DateFormatSymbols;
51eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport java.util.ArrayList;
52eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport java.util.Calendar;
53eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport java.util.Locale;
54eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
55eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio/**
56eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * View to show a clock circle picker (with one or two picking circles)
57eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio *
58eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * @hide
59eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio */
60eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Megliopublic class RadialTimePickerView extends View implements View.OnTouchListener {
61eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final String TAG = "ClockView";
62eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
63eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final boolean DEBUG = false;
64eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
65eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int DEBUG_COLOR = 0x20FF0000;
66eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int DEBUG_TEXT_COLOR = 0x60FF0000;
67eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int DEBUG_STROKE_WIDTH = 2;
68eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
69eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int HOURS = 0;
70eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int MINUTES = 1;
71eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int HOURS_INNER = 2;
72eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int AMPM = 3;
73eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
74eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int SELECTOR_CIRCLE = 0;
75eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int SELECTOR_DOT = 1;
76eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int SELECTOR_LINE = 2;
77eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
78eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int AM = 0;
79eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int PM = 1;
80eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
81eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Opaque alpha level
82eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int ALPHA_OPAQUE = 255;
83eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
84eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Transparent alpha level
85eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int ALPHA_TRANSPARENT = 0;
86eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
87eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Alpha level of color for selector.
88518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette    private static final int ALPHA_SELECTOR = 60; // was 51
89eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
90eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Alpha level of color for selected circle.
91eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int ALPHA_AMPM_SELECTED = ALPHA_SELECTOR;
92eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
93eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Alpha level of color for pressed circle.
94eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette    private static final int ALPHA_AMPM_PRESSED = 255; // was 175
95eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
96eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final float COSINE_30_DEGREES = ((float) Math.sqrt(3)) * 0.5f;
97eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final float SINE_30_DEGREES = 0.5f;
98eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
99eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int DEGREES_FOR_ONE_HOUR = 30;
100eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int DEGREES_FOR_ONE_MINUTE = 6;
101eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
102eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int[] HOURS_NUMBERS = {12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
103eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int[] HOURS_NUMBERS_24 = {0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23};
104eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int[] MINUTES_NUMBERS = {0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55};
105eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
106eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int CENTER_RADIUS = 2;
107eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
108518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette    private static final int[] STATE_SET_SELECTED = new int[] {R.attr.state_selected};
10960727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette
110eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static int[] sSnapPrefer30sMap = new int[361];
111eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
112eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private final String[] mHours12Texts = new String[12];
113eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private final String[] mOuterHours24Texts = new String[12];
114eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private final String[] mInnerHours24Texts = new String[12];
115eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private final String[] mMinutesTexts = new String[12];
116eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
117eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private final String[] mAmPmText = new String[2];
118eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
119eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private final Paint[] mPaint = new Paint[2];
120eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette    private final int[] mColor = new int[2];
121eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette    private final IntHolder[] mAlpha = new IntHolder[2];
122eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette
123eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private final Paint mPaintCenter = new Paint();
124eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette
125eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private final Paint[][] mPaintSelector = new Paint[2][3];
126eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette    private final int[][] mColorSelector = new int[2][3];
127eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette    private final IntHolder[][] mAlphaSelector = new IntHolder[2][3];
128eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette
129eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private final Paint mPaintAmPmText = new Paint();
130eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private final Paint[] mPaintAmPmCircle = new Paint[2];
131eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
132eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private final Paint mPaintBackground = new Paint();
133eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private final Paint mPaintDisabled = new Paint();
134eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private final Paint mPaintDebug = new Paint();
135eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
136eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private Typeface mTypeface;
137eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
138eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean mIs24HourMode;
139eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean mShowHours;
140eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean mIsOnInnerCircle;
141eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
142eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mXCenter;
143eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mYCenter;
144eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
145eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float[] mCircleRadius = new float[3];
146eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
147eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mMinHypotenuseForInnerNumber;
148eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mMaxHypotenuseForOuterNumber;
149eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mHalfwayHypotenusePoint;
150eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
151eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float[] mTextSize = new float[2];
152eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float mInnerTextSize;
153eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
154eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float[][] mTextGridHeights = new float[2][7];
155eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float[][] mTextGridWidths = new float[2][7];
156eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
157eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float[] mInnerTextGridHeights = new float[7];
158eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float[] mInnerTextGridWidths = new float[7];
159eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
160eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String[] mOuterTextHours;
161eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String[] mInnerTextHours;
162eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String[] mOuterTextMinutes;
163eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
164eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float[] mCircleRadiusMultiplier = new float[2];
165eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float[] mNumbersRadiusMultiplier = new float[3];
166eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
167eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float[] mTextSizeMultiplier = new float[3];
168eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
169eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float[] mAnimationRadiusMultiplier = new float[3];
170eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
171eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float mTransitionMidRadiusMultiplier;
172eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float mTransitionEndRadiusMultiplier;
173eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
174eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private AnimatorSet mTransition;
175eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private InvalidateUpdateListener mInvalidateUpdateListener = new InvalidateUpdateListener();
176eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
177eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int[] mLineLength = new int[3];
178eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int[] mSelectionRadius = new int[3];
179eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float mSelectionRadiusMultiplier;
180eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int[] mSelectionDegrees = new int[3];
181eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
182eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mAmPmCircleRadius;
183eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float mAmPmYCenter;
184eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
185eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float mAmPmCircleRadiusMultiplier;
186eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mAmPmTextColor;
187eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
188eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float mLeftIndicatorXCenter;
189eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private float mRightIndicatorXCenter;
190eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
191eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mAmPmUnselectedColor;
192eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mAmPmSelectedColor;
193eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
194eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mAmOrPm;
195eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mAmOrPmPressed;
196eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1975134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette    private int mDisabledAlpha;
1985134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette
199eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private RectF mRectF = new RectF();
200eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean mInputEnabled = true;
201eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private OnValueSelectedListener mListener;
202eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
203eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private final ArrayList<Animator> mHoursToMinutesAnims = new ArrayList<Animator>();
204eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private final ArrayList<Animator> mMinuteToHoursAnims = new ArrayList<Animator>();
205eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
206eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public interface OnValueSelectedListener {
207eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance);
208eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
209eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
210eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    static {
211eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Prepare mapping to snap touchable degrees to selectable degrees.
212eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        preparePrefer30sMap();
213eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
214eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
215eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
216eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Split up the 360 degrees of the circle among the 60 selectable values. Assigns a larger
217eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * selectable area to each of the 12 visible values, such that the ratio of space apportioned
218eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * to a visible value : space apportioned to a non-visible value will be 14 : 4.
219eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * E.g. the output of 30 degrees should have a higher range of input associated with it than
220eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * the output of 24 degrees, because 30 degrees corresponds to a visible number on the clock
221eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * circle (5 on the minutes, 1 or 13 on the hours).
222eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
223eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static void preparePrefer30sMap() {
224eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // We'll split up the visible output and the non-visible output such that each visible
225eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // output will correspond to a range of 14 associated input degrees, and each non-visible
226eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // output will correspond to a range of 4 associate input degrees, so visible numbers
227eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // are more than 3 times easier to get than non-visible numbers:
228eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // {354-359,0-7}:0, {8-11}:6, {12-15}:12, {16-19}:18, {20-23}:24, {24-37}:30, etc.
229eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        //
230eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // If an output of 30 degrees should correspond to a range of 14 associated degrees, then
231eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // we'll need any input between 24 - 37 to snap to 30. Working out from there, 20-23 should
232eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // snap to 24, while 38-41 should snap to 36. This is somewhat counter-intuitive, that you
233eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // can be touching 36 degrees but have the selection snapped to 30 degrees; however, this
234eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // inconsistency isn't noticeable at such fine-grained degrees, and it affords us the
235eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // ability to aggressively prefer the visible values by a factor of more than 3:1, which
236eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // greatly contributes to the selectability of these values.
237eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
238eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // The first output is 0, and each following output will increment by 6 {0, 6, 12, ...}.
239eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int snappedOutputDegrees = 0;
240eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Count of how many inputs we've designated to the specified output.
241eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int count = 1;
242eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // How many input we expect for a specified output. This will be 14 for output divisible
243eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // by 30, and 4 for the remaining output. We'll special case the outputs of 0 and 360, so
244eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // the caller can decide which they need.
245eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int expectedCount = 8;
246eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Iterate through the input.
247eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        for (int degrees = 0; degrees < 361; degrees++) {
248eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // Save the input-output mapping.
249eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            sSnapPrefer30sMap[degrees] = snappedOutputDegrees;
250eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // If this is the last input for the specified output, calculate the next output and
251eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // the next expected count.
252eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (count == expectedCount) {
253eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                snappedOutputDegrees += 6;
254eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (snappedOutputDegrees == 360) {
255eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    expectedCount = 7;
256eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                } else if (snappedOutputDegrees % 30 == 0) {
257eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    expectedCount = 14;
258eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                } else {
259eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    expectedCount = 4;
260eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
261eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                count = 1;
262eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else {
263eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                count++;
264eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
265eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
266eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
267eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
268eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
269eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Returns mapping of any input degrees (0 to 360) to one of 60 selectable output degrees,
270eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * where the degrees corresponding to visible numbers (i.e. those divisible by 30) will be
271eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * weighted heavier than the degrees corresponding to non-visible numbers.
272eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * See {@link #preparePrefer30sMap()} documentation for the rationale and generation of the
273eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * mapping.
274eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
275eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static int snapPrefer30s(int degrees) {
276eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (sSnapPrefer30sMap == null) {
277eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return -1;
278eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
279eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return sSnapPrefer30sMap[degrees];
280eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
281eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
282eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
283eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Returns mapping of any input degrees (0 to 360) to one of 12 visible output degrees (all
284eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * multiples of 30), where the input will be "snapped" to the closest visible degrees.
285eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param degrees The input degrees
286eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param forceHigherOrLower The output may be forced to either the higher or lower step, or may
287eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * be allowed to snap to whichever is closer. Use 1 to force strictly higher, -1 to force
288eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * strictly lower, and 0 to snap to the closer one.
289eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return output degrees, will be a multiple of 30
290eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
291eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static int snapOnly30s(int degrees, int forceHigherOrLower) {
292eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int stepSize = DEGREES_FOR_ONE_HOUR;
293eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int floor = (degrees / stepSize) * stepSize;
294eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int ceiling = floor + stepSize;
295eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (forceHigherOrLower == 1) {
296eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            degrees = ceiling;
297eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (forceHigherOrLower == -1) {
298eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (degrees == floor) {
299eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                floor -= stepSize;
300eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
301eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            degrees = floor;
302eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
303eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if ((degrees - floor) < (ceiling - degrees)) {
304eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                degrees = floor;
305eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else {
306eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                degrees = ceiling;
307eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
308eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
309eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return degrees;
310eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
311eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
312eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public RadialTimePickerView(Context context, AttributeSet attrs)  {
313eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        this(context, attrs, R.attr.timePickerStyle);
314eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
315eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
316eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public RadialTimePickerView(Context context, AttributeSet attrs, int defStyle)  {
317eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        super(context, attrs);
318eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
3195134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        // Pull disabled alpha from theme.
3205134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        final TypedValue outValue = new TypedValue();
3215134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
3225134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        mDisabledAlpha = (int) (outValue.getFloat() * 255 + 0.5f);
3235134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette
324eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // process style attributes
3255134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        final Resources res = getResources();
326eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.TimePicker,
327eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                defStyle, 0);
328eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
32960727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        ColorStateList amPmBackgroundColor = a.getColorStateList(
33060727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.styleable.TimePicker_amPmBackgroundColor);
33160727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        if (amPmBackgroundColor == null) {
33260727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette            amPmBackgroundColor = res.getColorStateList(
33360727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                    R.color.timepicker_default_ampm_unselected_background_color_material);
33460727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        }
33560727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette
33660727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        // Obtain the backup selected color. If the background color state
33760727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        // list doesn't have a state for selected, we'll use this color.
33860727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        final int amPmSelectedColor = a.getColor(R.styleable.TimePicker_amPmSelectedBackgroundColor,
339830960cce032a1b0dc0cf54bcc44ffa339388c21Alan Viverette                res.getColor(R.color.timepicker_default_ampm_selected_background_color_material));
340518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette        amPmBackgroundColor = ColorStateList.addFirstIfMissing(
341518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette                amPmBackgroundColor, R.attr.state_selected, amPmSelectedColor);
342518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette
34360727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mAmPmSelectedColor = amPmBackgroundColor.getColorForState(
34460727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                STATE_SET_SELECTED, amPmSelectedColor);
34560727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mAmPmUnselectedColor = amPmBackgroundColor.getDefaultColor();
346eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
347eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAmPmTextColor = a.getColor(R.styleable.TimePicker_amPmTextColor,
348830960cce032a1b0dc0cf54bcc44ffa339388c21Alan Viverette                res.getColor(R.color.timepicker_default_text_color_material));
349eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
350eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTypeface = Typeface.create("sans-serif", Typeface.NORMAL);
351eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
352eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        // Initialize all alpha values to opaque.
353eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        for (int i = 0; i < mAlpha.length; i++) {
354eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mAlpha[i] = new IntHolder(ALPHA_OPAQUE);
355eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        }
356eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        for (int i = 0; i < mAlphaSelector.length; i++) {
357eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            for (int j = 0; j < mAlphaSelector[i].length; j++) {
358eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette                mAlphaSelector[i][j] = new IntHolder(ALPHA_OPAQUE);
359eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            }
360eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        }
361eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette
362eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        final int numbersTextColor = a.getColor(R.styleable.TimePicker_numbersTextColor,
363830960cce032a1b0dc0cf54bcc44ffa339388c21Alan Viverette                res.getColor(R.color.timepicker_default_text_color_material));
364eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette
365eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaint[HOURS] = new Paint();
366eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaint[HOURS].setAntiAlias(true);
367eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaint[HOURS].setTextAlign(Paint.Align.CENTER);
368eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mColor[HOURS] = numbersTextColor;
369eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
370eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaint[MINUTES] = new Paint();
371eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaint[MINUTES].setAntiAlias(true);
372eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaint[MINUTES].setTextAlign(Paint.Align.CENTER);
373eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mColor[MINUTES] = numbersTextColor;
374eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
375eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintCenter.setColor(numbersTextColor);
376eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintCenter.setAntiAlias(true);
377eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintCenter.setTextAlign(Paint.Align.CENTER);
378eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
379eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintSelector[HOURS][SELECTOR_CIRCLE] = new Paint();
380eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintSelector[HOURS][SELECTOR_CIRCLE].setAntiAlias(true);
381eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mColorSelector[HOURS][SELECTOR_CIRCLE] = a.getColor(
382eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette                R.styleable.TimePicker_numbersSelectorColor,
383830960cce032a1b0dc0cf54bcc44ffa339388c21Alan Viverette                R.color.timepicker_default_selector_color_material);
384eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
385eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintSelector[HOURS][SELECTOR_DOT] = new Paint();
386eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintSelector[HOURS][SELECTOR_DOT].setAntiAlias(true);
387eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mColorSelector[HOURS][SELECTOR_DOT] = a.getColor(
388eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette                R.styleable.TimePicker_numbersSelectorColor,
389830960cce032a1b0dc0cf54bcc44ffa339388c21Alan Viverette                R.color.timepicker_default_selector_color_material);
390eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
391eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintSelector[HOURS][SELECTOR_LINE] = new Paint();
392eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintSelector[HOURS][SELECTOR_LINE].setAntiAlias(true);
393eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintSelector[HOURS][SELECTOR_LINE].setStrokeWidth(2);
394eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mColorSelector[HOURS][SELECTOR_LINE] = a.getColor(
395eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette                R.styleable.TimePicker_numbersSelectorColor,
396830960cce032a1b0dc0cf54bcc44ffa339388c21Alan Viverette                R.color.timepicker_default_selector_color_material);
397eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
398eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintSelector[MINUTES][SELECTOR_CIRCLE] = new Paint();
399eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintSelector[MINUTES][SELECTOR_CIRCLE].setAntiAlias(true);
400eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mColorSelector[MINUTES][SELECTOR_CIRCLE] = a.getColor(
401eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette                R.styleable.TimePicker_numbersSelectorColor,
402830960cce032a1b0dc0cf54bcc44ffa339388c21Alan Viverette                R.color.timepicker_default_selector_color_material);
403eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
404eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintSelector[MINUTES][SELECTOR_DOT] = new Paint();
405eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintSelector[MINUTES][SELECTOR_DOT].setAntiAlias(true);
406eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mColorSelector[MINUTES][SELECTOR_DOT] = a.getColor(
407eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette                R.styleable.TimePicker_numbersSelectorColor,
408830960cce032a1b0dc0cf54bcc44ffa339388c21Alan Viverette                R.color.timepicker_default_selector_color_material);
409eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
410eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintSelector[MINUTES][SELECTOR_LINE] = new Paint();
411eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintSelector[MINUTES][SELECTOR_LINE].setAntiAlias(true);
412eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintSelector[MINUTES][SELECTOR_LINE].setStrokeWidth(2);
413eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mColorSelector[MINUTES][SELECTOR_LINE] = a.getColor(
414eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette                R.styleable.TimePicker_numbersSelectorColor,
415830960cce032a1b0dc0cf54bcc44ffa339388c21Alan Viverette                R.color.timepicker_default_selector_color_material);
416eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
417eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintAmPmText.setColor(mAmPmTextColor);
418eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintAmPmText.setTypeface(mTypeface);
419eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintAmPmText.setAntiAlias(true);
420eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintAmPmText.setTextAlign(Paint.Align.CENTER);
421eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
422eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintAmPmCircle[AM] = new Paint();
423eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintAmPmCircle[AM].setAntiAlias(true);
424eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintAmPmCircle[PM] = new Paint();
425eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintAmPmCircle[PM].setAntiAlias(true);
426eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
427eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mPaintBackground.setColor(a.getColor(R.styleable.TimePicker_numbersBackgroundColor,
428830960cce032a1b0dc0cf54bcc44ffa339388c21Alan Viverette                res.getColor(R.color.timepicker_default_numbers_background_color_material)));
429eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintBackground.setAntiAlias(true);
430eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
431eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (DEBUG) {
432eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mPaintDebug.setColor(DEBUG_COLOR);
433eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mPaintDebug.setAntiAlias(true);
434eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mPaintDebug.setStrokeWidth(DEBUG_STROKE_WIDTH);
435eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mPaintDebug.setStyle(Paint.Style.STROKE);
436eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mPaintDebug.setTextAlign(Paint.Align.CENTER);
437eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
438eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
439eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mShowHours = true;
440eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mIs24HourMode = false;
441eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAmOrPm = AM;
442eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAmOrPmPressed = -1;
443eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
444eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        initHoursAndMinutesText();
445eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        initData();
446eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
447eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTransitionMidRadiusMultiplier =  Float.parseFloat(
448eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                res.getString(R.string.timepicker_transition_mid_radius_multiplier));
449eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTransitionEndRadiusMultiplier = Float.parseFloat(
450eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                res.getString(R.string.timepicker_transition_end_radius_multiplier));
451eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
452eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTextGridHeights[HOURS] = new float[7];
453eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTextGridHeights[MINUTES] = new float[7];
454eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
455eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mSelectionRadiusMultiplier = Float.parseFloat(
456eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                res.getString(R.string.timepicker_selection_radius_multiplier));
457eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
458eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        a.recycle();
459eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette
460eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        setOnTouchListener(this);
461ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette        setClickable(true);
462eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
463eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Initial values
464eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final Calendar calendar = Calendar.getInstance(Locale.getDefault());
465eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int currentHour = calendar.get(Calendar.HOUR_OF_DAY);
466eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int currentMinute = calendar.get(Calendar.MINUTE);
467eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
468eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        setCurrentHour(currentHour);
469eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        setCurrentMinute(currentMinute);
470eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
471eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        setHapticFeedbackEnabled(true);
472eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
473eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
474eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
475eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Measure the view to end up as a square, based on the minimum of the height and width.
476eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
477eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
478eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
479eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
480eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
481eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int measuredHeight = MeasureSpec.getSize(heightMeasureSpec);
482eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
483eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int minDimension = Math.min(measuredWidth, measuredHeight);
484eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
485eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        super.onMeasure(MeasureSpec.makeMeasureSpec(minDimension, widthMode),
486eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                MeasureSpec.makeMeasureSpec(minDimension, heightMode));
487eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
488eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
489eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void initialize(int hour, int minute, boolean is24HourMode) {
490eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mIs24HourMode = is24HourMode;
491eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        setCurrentHour(hour);
492eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        setCurrentMinute(minute);
493eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
494eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
495eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setCurrentItemShowing(int item, boolean animate) {
496eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        switch (item){
497eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case HOURS:
498eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                showHours(animate);
499eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                break;
500eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case MINUTES:
501eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                showMinutes(animate);
502eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                break;
503eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            default:
504eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                Log.e(TAG, "ClockView does not support showing item " + item);
505eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
506eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
507eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
508eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public int getCurrentItemShowing() {
509eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return mShowHours ? HOURS : MINUTES;
510eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
511eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
512eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setOnValueSelectedListener(OnValueSelectedListener listener) {
513eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mListener = listener;
514eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
515eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
516eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setCurrentHour(int hour) {
517eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int degrees = (hour % 12) * DEGREES_FOR_ONE_HOUR;
518eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mSelectionDegrees[HOURS] = degrees;
519eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mSelectionDegrees[HOURS_INNER] = degrees;
520eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAmOrPm = ((hour % 24) < 12) ? AM : PM;
521eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourMode) {
522eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mIsOnInnerCircle = (mAmOrPm == AM);
523eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
524eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mIsOnInnerCircle = false;
525eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
526eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        initData();
527eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateLayoutData();
528eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        invalidate();
529eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
530eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
531eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Return hours in 0-23 range
532eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public int getCurrentHour() {
533eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int hours =
534eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mSelectionDegrees[mIsOnInnerCircle ? HOURS_INNER : HOURS] / DEGREES_FOR_ONE_HOUR;
535eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourMode) {
536eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mIsOnInnerCircle) {
537eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                hours = hours % 12;
5383fbad42a4b315827df8159fcb74b78ef055aa3efFabrice Di Meglio                if (hours == 0) {
5393fbad42a4b315827df8159fcb74b78ef055aa3efFabrice Di Meglio                    hours = 12;
5403fbad42a4b315827df8159fcb74b78ef055aa3efFabrice Di Meglio                }
541eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else {
542eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (hours != 0) {
543eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    hours += 12;
544eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
545eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
546eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
547eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            hours = hours % 12;
548eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (hours == 0) {
549eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (mAmOrPm == PM) {
550eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    hours = 12;
551eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
552eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else {
553eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (mAmOrPm == PM) {
554eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    hours += 12;
555eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
556eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
557eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
558eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return hours;
559eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
560eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
561eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setCurrentMinute(int minute) {
562eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mSelectionDegrees[MINUTES] = (minute % 60) * DEGREES_FOR_ONE_MINUTE;
563eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        invalidate();
564eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
565eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
566eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Returns minutes in 0-59 range
567eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public int getCurrentMinute() {
568eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return (mSelectionDegrees[MINUTES] / DEGREES_FOR_ONE_MINUTE);
569eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
570eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
571eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setAmOrPm(int val) {
572eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAmOrPm = (val % 2);
573eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        invalidate();
574eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
575eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
576eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public int getAmOrPm() {
577eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return mAmOrPm;
578eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
579eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
580eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void swapAmPm() {
581eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAmOrPm = (mAmOrPm == AM) ? PM : AM;
582eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        invalidate();
583eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
584eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
585eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void showHours(boolean animate) {
586eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mShowHours) return;
587eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mShowHours = true;
588eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (animate) {
589eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            startMinutesToHoursAnimation();
590eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
591eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        initData();
592eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateLayoutData();
593eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        invalidate();
594eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
595eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
596eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void showMinutes(boolean animate) {
597eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (!mShowHours) return;
598eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mShowHours = false;
599eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (animate) {
600eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            startHoursToMinutesAnimation();
601eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
602eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        initData();
603eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateLayoutData();
604eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        invalidate();
605eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
606eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
607eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void initHoursAndMinutesText() {
608eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Initialize the hours and minutes numbers.
609eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        for (int i = 0; i < 12; i++) {
610eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mHours12Texts[i] = String.format("%d", HOURS_NUMBERS[i]);
611eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mOuterHours24Texts[i] = String.format("%02d", HOURS_NUMBERS_24[i]);
612eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mInnerHours24Texts[i] = String.format("%d", HOURS_NUMBERS[i]);
613eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mMinutesTexts[i] = String.format("%02d", MINUTES_NUMBERS[i]);
614eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
615eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
616ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette        String[] amPmStrings = TimePickerClockDelegate.getAmPmStrings(mContext);
617ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette        mAmPmText[AM] = amPmStrings[0];
618ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette        mAmPmText[PM] = amPmStrings[1];
619eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
620eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
621eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void initData() {
622eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourMode) {
623eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mOuterTextHours = mOuterHours24Texts;
624eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mInnerTextHours = mInnerHours24Texts;
625eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
626eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mOuterTextHours = mHours12Texts;
627eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mInnerTextHours = null;
628eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
629eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
630eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mOuterTextMinutes = mMinutesTexts;
631eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
632eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final Resources res = getResources();
633eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
634eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mShowHours) {
635eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mIs24HourMode) {
636eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mCircleRadiusMultiplier[HOURS] = Float.parseFloat(
637eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        res.getString(R.string.timepicker_circle_radius_multiplier_24HourMode));
638eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mNumbersRadiusMultiplier[HOURS] = Float.parseFloat(
639eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        res.getString(R.string.timepicker_numbers_radius_multiplier_outer));
640eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mTextSizeMultiplier[HOURS] = Float.parseFloat(
641eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        res.getString(R.string.timepicker_text_size_multiplier_outer));
642eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
643eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mNumbersRadiusMultiplier[HOURS_INNER] = Float.parseFloat(
644eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        res.getString(R.string.timepicker_numbers_radius_multiplier_inner));
645eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mTextSizeMultiplier[HOURS_INNER] = Float.parseFloat(
646eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        res.getString(R.string.timepicker_text_size_multiplier_inner));
647eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else {
648eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mCircleRadiusMultiplier[HOURS] = Float.parseFloat(
649eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        res.getString(R.string.timepicker_circle_radius_multiplier));
650eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mNumbersRadiusMultiplier[HOURS] = Float.parseFloat(
651eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        res.getString(R.string.timepicker_numbers_radius_multiplier_normal));
652eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mTextSizeMultiplier[HOURS] = Float.parseFloat(
653eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        res.getString(R.string.timepicker_text_size_multiplier_normal));
654eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
655eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
656eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mCircleRadiusMultiplier[MINUTES] = Float.parseFloat(
657eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    res.getString(R.string.timepicker_circle_radius_multiplier));
658eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mNumbersRadiusMultiplier[MINUTES] = Float.parseFloat(
659eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    res.getString(R.string.timepicker_numbers_radius_multiplier_normal));
660eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mTextSizeMultiplier[MINUTES] = Float.parseFloat(
661eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    res.getString(R.string.timepicker_text_size_multiplier_normal));
662eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
663eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
664eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAnimationRadiusMultiplier[HOURS] = 1;
665eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAnimationRadiusMultiplier[HOURS_INNER] = 1;
666eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAnimationRadiusMultiplier[MINUTES] = 1;
667eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
668eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAmPmCircleRadiusMultiplier = Float.parseFloat(
669eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                res.getString(R.string.timepicker_ampm_circle_radius_multiplier));
670eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
671eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mAlpha[HOURS].setValue(mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT);
672eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mAlpha[MINUTES].setValue(mShowHours ? ALPHA_TRANSPARENT : ALPHA_OPAQUE);
673eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
674eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mAlphaSelector[HOURS][SELECTOR_CIRCLE].setValue(
675eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette                mShowHours ? ALPHA_SELECTOR : ALPHA_TRANSPARENT);
676eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mAlphaSelector[HOURS][SELECTOR_DOT].setValue(
677eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT);
678eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mAlphaSelector[HOURS][SELECTOR_LINE].setValue(
679eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mShowHours ? ALPHA_SELECTOR : ALPHA_TRANSPARENT);
680eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
681eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mAlphaSelector[MINUTES][SELECTOR_CIRCLE].setValue(
682eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mShowHours ? ALPHA_TRANSPARENT : ALPHA_SELECTOR);
683eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mAlphaSelector[MINUTES][SELECTOR_DOT].setValue(
684eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mShowHours ? ALPHA_TRANSPARENT : ALPHA_OPAQUE);
685eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mAlphaSelector[MINUTES][SELECTOR_LINE].setValue(
686eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mShowHours ? ALPHA_TRANSPARENT : ALPHA_SELECTOR);
687eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
688eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
689eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
690eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
691eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateLayoutData();
692eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
693eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
694eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateLayoutData() {
695eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mXCenter = getWidth() / 2;
696eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mYCenter = getHeight() / 2;
697eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
698eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int min = Math.min(mXCenter, mYCenter);
699eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
700eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mCircleRadius[HOURS] = min * mCircleRadiusMultiplier[HOURS];
701eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mCircleRadius[HOURS_INNER] = min * mCircleRadiusMultiplier[HOURS];
702eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mCircleRadius[MINUTES] = min * mCircleRadiusMultiplier[MINUTES];
703eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
704eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (!mIs24HourMode) {
705eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We'll need to draw the AM/PM circles, so the main circle will need to have
706eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // a slightly higher center. To keep the entire view centered vertically, we'll
707eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // have to push it up by half the radius of the AM/PM circles.
708eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int amPmCircleRadius = (int) (mCircleRadius[HOURS] * mAmPmCircleRadiusMultiplier);
709eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mYCenter -= amPmCircleRadius / 2;
710eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
711eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
712eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mMinHypotenuseForInnerNumber = (int) (mCircleRadius[HOURS]
713eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                * mNumbersRadiusMultiplier[HOURS_INNER]) - mSelectionRadius[HOURS];
714eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mMaxHypotenuseForOuterNumber = (int) (mCircleRadius[HOURS]
715eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                * mNumbersRadiusMultiplier[HOURS]) + mSelectionRadius[HOURS];
716eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mHalfwayHypotenusePoint = (int) (mCircleRadius[HOURS]
717eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                * ((mNumbersRadiusMultiplier[HOURS] + mNumbersRadiusMultiplier[HOURS_INNER]) / 2));
718eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
719eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTextSize[HOURS] = mCircleRadius[HOURS] * mTextSizeMultiplier[HOURS];
720eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTextSize[MINUTES] = mCircleRadius[MINUTES] * mTextSizeMultiplier[MINUTES];
721eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
722eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourMode) {
723eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mInnerTextSize = mCircleRadius[HOURS] * mTextSizeMultiplier[HOURS_INNER];
724eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
725eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
726eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        calculateGridSizesHours();
727eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        calculateGridSizesMinutes();
728eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
729eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mSelectionRadius[HOURS] = (int) (mCircleRadius[HOURS] * mSelectionRadiusMultiplier);
730eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mSelectionRadius[HOURS_INNER] = mSelectionRadius[HOURS];
731eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mSelectionRadius[MINUTES] = (int) (mCircleRadius[MINUTES] * mSelectionRadiusMultiplier);
732eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
733eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAmPmCircleRadius = (int) (mCircleRadius[HOURS] * mAmPmCircleRadiusMultiplier);
734eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintAmPmText.setTextSize(mAmPmCircleRadius * 3 / 4);
735eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
736eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Line up the vertical center of the AM/PM circles with the bottom of the main circle.
737eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAmPmYCenter = mYCenter + mCircleRadius[HOURS];
738eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
739eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Line up the horizontal edges of the AM/PM circles with the horizontal edges
740eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // of the main circle
741eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mLeftIndicatorXCenter = mXCenter - mCircleRadius[HOURS] + mAmPmCircleRadius;
742eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRightIndicatorXCenter = mXCenter + mCircleRadius[HOURS] - mAmPmCircleRadius;
743eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
744eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
745eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
746eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void onDraw(Canvas canvas) {
7475134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        if (!mInputEnabled) {
7485134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette            canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), mDisabledAlpha);
7495134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        } else {
7505134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette            canvas.save();
7515134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        }
752eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
753eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        calculateGridSizesHours();
754eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        calculateGridSizesMinutes();
755eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
756eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        drawCircleBackground(canvas);
757eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        drawSelector(canvas);
758eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
759eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        drawTextElements(canvas, mTextSize[HOURS], mTypeface, mOuterTextHours,
760eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette                mTextGridWidths[HOURS], mTextGridHeights[HOURS], mPaint[HOURS],
761eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette                mColor[HOURS], mAlpha[HOURS].getValue());
762eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
763eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourMode && mInnerTextHours != null) {
764eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            drawTextElements(canvas, mInnerTextSize, mTypeface, mInnerTextHours,
765eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette                    mInnerTextGridWidths, mInnerTextGridHeights, mPaint[HOURS],
766eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette                    mColor[HOURS], mAlpha[HOURS].getValue());
767eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
768eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
769eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mOuterTextMinutes,
770eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette                mTextGridWidths[MINUTES], mTextGridHeights[MINUTES], mPaint[MINUTES],
771eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette                mColor[MINUTES], mAlpha[MINUTES].getValue());
772eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
773eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        drawCenter(canvas);
774eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (!mIs24HourMode) {
775eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            drawAmPm(canvas);
776eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
777eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
778eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (DEBUG) {
779eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            drawDebug(canvas);
780eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
781eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
782eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.restore();
783eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
784eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
785eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void drawCircleBackground(Canvas canvas) {
786eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawCircle(mXCenter, mYCenter, mCircleRadius[HOURS], mPaintBackground);
787eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
788eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
789eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void drawCenter(Canvas canvas) {
790eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawCircle(mXCenter, mYCenter, CENTER_RADIUS, mPaintCenter);
791eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
792eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
793eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void drawSelector(Canvas canvas) {
794eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        drawSelector(canvas, mIsOnInnerCircle ? HOURS_INNER : HOURS);
795eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        drawSelector(canvas, MINUTES);
796eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
797eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
798eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void drawAmPm(Canvas canvas) {
799eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final boolean isLayoutRtl = isLayoutRtl();
800eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
801eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int amColor = mAmPmUnselectedColor;
802eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int amAlpha = ALPHA_OPAQUE;
803eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int pmColor = mAmPmUnselectedColor;
804eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int pmAlpha = ALPHA_OPAQUE;
805eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mAmOrPm == AM) {
806eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            amColor = mAmPmSelectedColor;
807eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            amAlpha = ALPHA_AMPM_SELECTED;
808eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (mAmOrPm == PM) {
809eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            pmColor = mAmPmSelectedColor;
810eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            pmAlpha = ALPHA_AMPM_SELECTED;
811eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
812eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mAmOrPmPressed == AM) {
813eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            amColor = mAmPmSelectedColor;
814eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            amAlpha = ALPHA_AMPM_PRESSED;
815eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (mAmOrPmPressed == PM) {
816eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            pmColor = mAmPmSelectedColor;
817eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            pmAlpha = ALPHA_AMPM_PRESSED;
818eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
819eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
820eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Draw the two circles
821eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintAmPmCircle[AM].setColor(amColor);
822eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mPaintAmPmCircle[AM].setAlpha(getMultipliedAlpha(amColor, amAlpha));
823eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawCircle(isLayoutRtl ? mRightIndicatorXCenter : mLeftIndicatorXCenter,
824eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mAmPmYCenter, mAmPmCircleRadius, mPaintAmPmCircle[AM]);
825eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
826eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintAmPmCircle[PM].setColor(pmColor);
827eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        mPaintAmPmCircle[PM].setAlpha(getMultipliedAlpha(pmColor, pmAlpha));
828eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawCircle(isLayoutRtl ? mLeftIndicatorXCenter : mRightIndicatorXCenter,
829eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mAmPmYCenter, mAmPmCircleRadius, mPaintAmPmCircle[PM]);
830eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
831eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Draw the AM/PM texts on top
832eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPaintAmPmText.setColor(mAmPmTextColor);
833eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float textYCenter = mAmPmYCenter -
834eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                (int) (mPaintAmPmText.descent() + mPaintAmPmText.ascent()) / 2;
835eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
836eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawText(isLayoutRtl ? mAmPmText[PM] : mAmPmText[AM], mLeftIndicatorXCenter,
837eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                textYCenter, mPaintAmPmText);
838eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawText(isLayoutRtl ? mAmPmText[AM] : mAmPmText[PM], mRightIndicatorXCenter,
839eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                textYCenter, mPaintAmPmText);
840eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
841eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
842eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette    private int getMultipliedAlpha(int argb, int alpha) {
843eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        return (int) (Color.alpha(argb) * (alpha / 255.0) + 0.5);
844eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette    }
845eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette
846eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void drawSelector(Canvas canvas, int index) {
847eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Calculate the current radius at which to place the selection circle.
848eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mLineLength[index] = (int) (mCircleRadius[index]
849eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                * mNumbersRadiusMultiplier[index] * mAnimationRadiusMultiplier[index]);
850eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
851eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        double selectionRadians = Math.toRadians(mSelectionDegrees[index]);
852eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
853eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int pointX = mXCenter + (int) (mLineLength[index] * Math.sin(selectionRadians));
854eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int pointY = mYCenter - (int) (mLineLength[index] * Math.cos(selectionRadians));
855eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
856eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        int color;
857eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        int alpha;
858eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        Paint paint;
859eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette
860eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Draw the selection circle
861eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        color = mColorSelector[index % 2][SELECTOR_CIRCLE];
862eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        alpha = mAlphaSelector[index % 2][SELECTOR_CIRCLE].getValue();
863eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        paint = mPaintSelector[index % 2][SELECTOR_CIRCLE];
864eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        paint.setColor(color);
865eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        paint.setAlpha(getMultipliedAlpha(color, alpha));
866eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        canvas.drawCircle(pointX, pointY, mSelectionRadius[index], paint);
867eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
868eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Draw the dot if needed
869eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mSelectionDegrees[index] % 30 != 0) {
870eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We're not on a direct tick
871eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            color = mColorSelector[index % 2][SELECTOR_DOT];
872eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            alpha = mAlphaSelector[index % 2][SELECTOR_DOT].getValue();
873eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            paint = mPaintSelector[index % 2][SELECTOR_DOT];
874eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            paint.setColor(color);
875eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            paint.setAlpha(getMultipliedAlpha(color, alpha));
876eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            canvas.drawCircle(pointX, pointY, (mSelectionRadius[index] * 2 / 7), paint);
877eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
878eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We're not drawing the dot, so shorten the line to only go as far as the edge of the
879eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // selection circle
880eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int lineLength = mLineLength[index] - mSelectionRadius[index];
881eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            pointX = mXCenter + (int) (lineLength * Math.sin(selectionRadians));
882eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            pointY = mYCenter - (int) (lineLength * Math.cos(selectionRadians));
883eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
884eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
885eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Draw the line
886eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        color = mColorSelector[index % 2][SELECTOR_LINE];
887eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        alpha = mAlphaSelector[index % 2][SELECTOR_LINE].getValue();
888eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        paint = mPaintSelector[index % 2][SELECTOR_LINE];
889eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        paint.setColor(color);
890eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        paint.setAlpha(getMultipliedAlpha(color, alpha));
891eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        canvas.drawLine(mXCenter, mYCenter, pointX, pointY, paint);
892eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
893eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
894eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void drawDebug(Canvas canvas) {
895eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Draw outer numbers circle
896eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final float outerRadius = mCircleRadius[HOURS] * mNumbersRadiusMultiplier[HOURS];
897eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawCircle(mXCenter, mYCenter, outerRadius, mPaintDebug);
898eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
899eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Draw inner numbers circle
900eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final float innerRadius = mCircleRadius[HOURS] * mNumbersRadiusMultiplier[HOURS_INNER];
901eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawCircle(mXCenter, mYCenter, innerRadius, mPaintDebug);
902eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
903eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Draw outer background circle
904eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawCircle(mXCenter, mYCenter, mCircleRadius[HOURS], mPaintDebug);
905eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
906eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Draw outer rectangle for circles
907eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float left = mXCenter - outerRadius;
908eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float top = mYCenter - outerRadius;
909eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float right = mXCenter + outerRadius;
910eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float bottom = mYCenter + outerRadius;
911eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRectF = new RectF(left, top, right, bottom);
912eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawRect(mRectF, mPaintDebug);
913eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
914eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Draw outer rectangle for background
915eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        left = mXCenter - mCircleRadius[HOURS];
916eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        top = mYCenter - mCircleRadius[HOURS];
917eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        right = mXCenter + mCircleRadius[HOURS];
918eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        bottom = mYCenter + mCircleRadius[HOURS];
919eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRectF.set(left, top, right, bottom);
920eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawRect(mRectF, mPaintDebug);
921eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
922eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Draw outer view rectangle
923eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRectF.set(0, 0, getWidth(), getHeight());
924eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawRect(mRectF, mPaintDebug);
925eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
926eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Draw selected time
927eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final String selected = String.format("%02d:%02d", getCurrentHour(), getCurrentMinute());
928eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
929eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
930eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                ViewGroup.LayoutParams.WRAP_CONTENT);
931eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        TextView tv = new TextView(getContext());
932eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        tv.setLayoutParams(lp);
933eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        tv.setText(selected);
934eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        tv.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
935eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        Paint paint = tv.getPaint();
936eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        paint.setColor(DEBUG_TEXT_COLOR);
937eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
938eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int width = tv.getMeasuredWidth();
939eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
940eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float height = paint.descent() - paint.ascent();
941eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float x = mXCenter - width / 2;
942eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float y = mYCenter + 1.5f * height;
943eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
944eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawText(selected.toString(), x, y, paint);
945eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
946eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
947eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void calculateGridSizesHours() {
948eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Calculate the text positions
949eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float numbersRadius = mCircleRadius[HOURS]
950eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                * mNumbersRadiusMultiplier[HOURS] * mAnimationRadiusMultiplier[HOURS];
951eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
952eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Calculate the positions for the 12 numbers in the main circle.
953eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        calculateGridSizes(mPaint[HOURS], numbersRadius, mXCenter, mYCenter,
954eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mTextSize[HOURS], mTextGridHeights[HOURS], mTextGridWidths[HOURS]);
955eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
956eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // If we have an inner circle, calculate those positions too.
957eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourMode) {
958eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            float innerNumbersRadius = mCircleRadius[HOURS_INNER]
959eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    * mNumbersRadiusMultiplier[HOURS_INNER]
960eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    * mAnimationRadiusMultiplier[HOURS_INNER];
961eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
962eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            calculateGridSizes(mPaint[HOURS], innerNumbersRadius, mXCenter, mYCenter,
963eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    mInnerTextSize, mInnerTextGridHeights, mInnerTextGridWidths);
964eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
965eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
966eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
967eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void calculateGridSizesMinutes() {
968eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Calculate the text positions
969eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float numbersRadius = mCircleRadius[MINUTES]
970eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                * mNumbersRadiusMultiplier[MINUTES] * mAnimationRadiusMultiplier[MINUTES];
971eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
972eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Calculate the positions for the 12 numbers in the main circle.
973eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        calculateGridSizes(mPaint[MINUTES], numbersRadius, mXCenter, mYCenter,
974eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mTextSize[MINUTES], mTextGridHeights[MINUTES], mTextGridWidths[MINUTES]);
975eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
976eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
977eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
978eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
979eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Using the trigonometric Unit Circle, calculate the positions that the text will need to be
980eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * drawn at based on the specified circle radius. Place the values in the textGridHeights and
981eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * textGridWidths parameters.
982eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
983eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static void calculateGridSizes(Paint paint, float numbersRadius, float xCenter,
984eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            float yCenter, float textSize, float[] textGridHeights, float[] textGridWidths) {
985eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        /*
986eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio         * The numbers need to be drawn in a 7x7 grid, representing the points on the Unit Circle.
987eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio         */
988eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final float offset1 = numbersRadius;
989eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // cos(30) = a / r => r * cos(30)
990eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final float offset2 = numbersRadius * COSINE_30_DEGREES;
991eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // sin(30) = o / r => r * sin(30)
992eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final float offset3 = numbersRadius * SINE_30_DEGREES;
993eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
994eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        paint.setTextSize(textSize);
995eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // We'll need yTextBase to be slightly lower to account for the text's baseline.
996eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        yCenter -= (paint.descent() + paint.ascent()) / 2;
997eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
998eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        textGridHeights[0] = yCenter - offset1;
999eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        textGridWidths[0] = xCenter - offset1;
1000eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        textGridHeights[1] = yCenter - offset2;
1001eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        textGridWidths[1] = xCenter - offset2;
1002eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        textGridHeights[2] = yCenter - offset3;
1003eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        textGridWidths[2] = xCenter - offset3;
1004eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        textGridHeights[3] = yCenter;
1005eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        textGridWidths[3] = xCenter;
1006eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        textGridHeights[4] = yCenter + offset3;
1007eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        textGridWidths[4] = xCenter + offset3;
1008eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        textGridHeights[5] = yCenter + offset2;
1009eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        textGridWidths[5] = xCenter + offset2;
1010eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        textGridHeights[6] = yCenter + offset1;
1011eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        textGridWidths[6] = xCenter + offset1;
1012eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1013eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1014eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
1015eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Draw the 12 text values at the positions specified by the textGrid parameters.
1016eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
1017eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void drawTextElements(Canvas canvas, float textSize, Typeface typeface, String[] texts,
1018eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            float[] textGridWidths, float[] textGridHeights, Paint paint, int color, int alpha) {
1019eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        paint.setTextSize(textSize);
1020eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        paint.setTypeface(typeface);
1021eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        paint.setColor(color);
1022eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        paint.setAlpha(getMultipliedAlpha(color, alpha));
1023eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawText(texts[0], textGridWidths[3], textGridHeights[0], paint);
1024eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawText(texts[1], textGridWidths[4], textGridHeights[1], paint);
1025eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawText(texts[2], textGridWidths[5], textGridHeights[2], paint);
1026eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawText(texts[3], textGridWidths[6], textGridHeights[3], paint);
1027eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawText(texts[4], textGridWidths[5], textGridHeights[4], paint);
1028eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawText(texts[5], textGridWidths[4], textGridHeights[5], paint);
1029eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawText(texts[6], textGridWidths[3], textGridHeights[6], paint);
1030eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawText(texts[7], textGridWidths[2], textGridHeights[5], paint);
1031eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawText(texts[8], textGridWidths[1], textGridHeights[4], paint);
1032eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawText(texts[9], textGridWidths[0], textGridHeights[3], paint);
1033eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawText(texts[10], textGridWidths[1], textGridHeights[2], paint);
1034eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        canvas.drawText(texts[11], textGridWidths[2], textGridHeights[1], paint);
1035eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1036eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1037eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Used for animating the hours by changing their radius
1038eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void setAnimationRadiusMultiplierHours(float animationRadiusMultiplier) {
1039eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAnimationRadiusMultiplier[HOURS] = animationRadiusMultiplier;
1040eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAnimationRadiusMultiplier[HOURS_INNER] = animationRadiusMultiplier;
1041eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1042eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1043eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Used for animating the minutes by changing their radius
1044eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void setAnimationRadiusMultiplierMinutes(float animationRadiusMultiplier) {
1045eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAnimationRadiusMultiplier[MINUTES] = animationRadiusMultiplier;
1046eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1047eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1048eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static ObjectAnimator getRadiusDisappearAnimator(Object target,
1049eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            String radiusPropertyName, InvalidateUpdateListener updateListener,
1050eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            float midRadiusMultiplier, float endRadiusMultiplier) {
1051eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        Keyframe kf0, kf1, kf2;
1052eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float midwayPoint = 0.2f;
1053eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int duration = 500;
1054eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1055eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        kf0 = Keyframe.ofFloat(0f, 1);
1056eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        kf1 = Keyframe.ofFloat(midwayPoint, midRadiusMultiplier);
1057eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        kf2 = Keyframe.ofFloat(1f, endRadiusMultiplier);
1058eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        PropertyValuesHolder radiusDisappear = PropertyValuesHolder.ofKeyframe(
1059eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                radiusPropertyName, kf0, kf1, kf2);
1060eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1061eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(
1062eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                target, radiusDisappear).setDuration(duration);
1063eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        animator.addUpdateListener(updateListener);
1064eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return animator;
1065eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1066eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1067eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static ObjectAnimator getRadiusReappearAnimator(Object target,
1068eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            String radiusPropertyName, InvalidateUpdateListener updateListener,
1069eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            float midRadiusMultiplier, float endRadiusMultiplier) {
1070eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        Keyframe kf0, kf1, kf2, kf3;
1071eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float midwayPoint = 0.2f;
1072eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int duration = 500;
1073eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1074eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Set up animator for reappearing.
1075eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float delayMultiplier = 0.25f;
1076eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float transitionDurationMultiplier = 1f;
1077eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float totalDurationMultiplier = transitionDurationMultiplier + delayMultiplier;
1078eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int totalDuration = (int) (duration * totalDurationMultiplier);
1079eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float delayPoint = (delayMultiplier * duration) / totalDuration;
1080eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        midwayPoint = 1 - (midwayPoint * (1 - delayPoint));
1081eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1082eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        kf0 = Keyframe.ofFloat(0f, endRadiusMultiplier);
1083eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        kf1 = Keyframe.ofFloat(delayPoint, endRadiusMultiplier);
1084eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        kf2 = Keyframe.ofFloat(midwayPoint, midRadiusMultiplier);
1085eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        kf3 = Keyframe.ofFloat(1f, 1);
1086eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        PropertyValuesHolder radiusReappear = PropertyValuesHolder.ofKeyframe(
1087eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                radiusPropertyName, kf0, kf1, kf2, kf3);
1088eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1089eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(
1090eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                target, radiusReappear).setDuration(totalDuration);
1091eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        animator.addUpdateListener(updateListener);
1092eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return animator;
1093eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1094eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1095eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette    private static ObjectAnimator getFadeOutAnimator(IntHolder target, int startAlpha, int endAlpha,
1096eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                InvalidateUpdateListener updateListener) {
1097eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int duration = 500;
1098eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        ObjectAnimator animator = ObjectAnimator.ofInt(target, "value", startAlpha, endAlpha);
1099eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        animator.setDuration(duration);
1100eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        animator.addUpdateListener(updateListener);
1101eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1102eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return animator;
1103eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1104eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1105eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette    private static ObjectAnimator getFadeInAnimator(IntHolder target, int startAlpha, int endAlpha,
1106eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                InvalidateUpdateListener updateListener) {
1107eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        Keyframe kf0, kf1, kf2;
1108eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int duration = 500;
1109eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1110eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Set up animator for reappearing.
1111eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float delayMultiplier = 0.25f;
1112eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float transitionDurationMultiplier = 1f;
1113eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float totalDurationMultiplier = transitionDurationMultiplier + delayMultiplier;
1114eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int totalDuration = (int) (duration * totalDurationMultiplier);
1115eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        float delayPoint = (delayMultiplier * duration) / totalDuration;
1116eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1117eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        kf0 = Keyframe.ofInt(0f, startAlpha);
1118eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        kf1 = Keyframe.ofInt(delayPoint, startAlpha);
1119eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        kf2 = Keyframe.ofInt(1f, endAlpha);
1120eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        PropertyValuesHolder fadeIn = PropertyValuesHolder.ofKeyframe("value", kf0, kf1, kf2);
1121eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1122eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(
1123eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                target, fadeIn).setDuration(totalDuration);
1124eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        animator.addUpdateListener(updateListener);
1125eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return animator;
1126eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1127eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1128eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private class InvalidateUpdateListener implements ValueAnimator.AnimatorUpdateListener {
1129eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        @Override
1130eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public void onAnimationUpdate(ValueAnimator animation) {
1131eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            RadialTimePickerView.this.invalidate();
1132eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1133eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1134eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1135eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void startHoursToMinutesAnimation() {
1136eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mHoursToMinutesAnims.size() == 0) {
1137eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mHoursToMinutesAnims.add(getRadiusDisappearAnimator(this,
1138eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    "animationRadiusMultiplierHours", mInvalidateUpdateListener,
1139eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    mTransitionMidRadiusMultiplier, mTransitionEndRadiusMultiplier));
1140eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mHoursToMinutesAnims.add(getFadeOutAnimator(mAlpha[HOURS],
1141eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
1142eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_CIRCLE],
1143eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_SELECTOR, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
1144eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_DOT],
1145eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
1146eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_LINE],
1147eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_SELECTOR, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
1148eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1149eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mHoursToMinutesAnims.add(getRadiusReappearAnimator(this,
1150eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    "animationRadiusMultiplierMinutes", mInvalidateUpdateListener,
1151eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    mTransitionMidRadiusMultiplier, mTransitionEndRadiusMultiplier));
1152eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mHoursToMinutesAnims.add(getFadeInAnimator(mAlpha[MINUTES],
1153eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
1154eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_CIRCLE],
1155eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_TRANSPARENT, ALPHA_SELECTOR, mInvalidateUpdateListener));
1156eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_DOT],
1157eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
1158eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_LINE],
1159eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_TRANSPARENT, ALPHA_SELECTOR, mInvalidateUpdateListener));
1160eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1161eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1162eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mTransition != null && mTransition.isRunning()) {
1163eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mTransition.end();
1164eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1165eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTransition = new AnimatorSet();
1166eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTransition.playTogether(mHoursToMinutesAnims);
1167eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTransition.start();
1168eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1169eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1170eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void startMinutesToHoursAnimation() {
1171eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mMinuteToHoursAnims.size() == 0) {
1172eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mMinuteToHoursAnims.add(getRadiusDisappearAnimator(this,
1173eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    "animationRadiusMultiplierMinutes", mInvalidateUpdateListener,
1174eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    mTransitionMidRadiusMultiplier, mTransitionEndRadiusMultiplier));
1175eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mMinuteToHoursAnims.add(getFadeOutAnimator(mAlpha[MINUTES],
1176eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
1177eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_CIRCLE],
1178eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_SELECTOR, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
1179eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_DOT],
1180eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
1181eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_LINE],
1182eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_SELECTOR, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
1183eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1184eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mMinuteToHoursAnims.add(getRadiusReappearAnimator(this,
1185eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    "animationRadiusMultiplierHours", mInvalidateUpdateListener,
1186eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    mTransitionMidRadiusMultiplier, mTransitionEndRadiusMultiplier));
1187eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mMinuteToHoursAnims.add(getFadeInAnimator(mAlpha[HOURS],
1188eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
1189eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_CIRCLE],
1190eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_TRANSPARENT, ALPHA_SELECTOR, mInvalidateUpdateListener));
1191eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_DOT],
1192eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
1193eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_LINE],
1194eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    ALPHA_TRANSPARENT, ALPHA_SELECTOR, mInvalidateUpdateListener));
1195eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1196eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1197eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mTransition != null && mTransition.isRunning()) {
1198eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mTransition.end();
1199eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1200eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTransition = new AnimatorSet();
1201eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTransition.playTogether(mMinuteToHoursAnims);
1202eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTransition.start();
1203eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1204eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1205eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int getDegreesFromXY(float x, float y) {
1206eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final double hypotenuse = Math.sqrt(
1207eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                (y - mYCenter) * (y - mYCenter) + (x - mXCenter) * (x - mXCenter));
1208eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1209eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Basic check if we're outside the range of the disk
1210eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (hypotenuse > mCircleRadius[HOURS]) {
1211eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return -1;
1212eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1213eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Check
1214eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourMode && mShowHours) {
1215eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (hypotenuse >= mMinHypotenuseForInnerNumber
1216eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    && hypotenuse <= mHalfwayHypotenusePoint) {
1217eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mIsOnInnerCircle = true;
1218eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else if (hypotenuse <= mMaxHypotenuseForOuterNumber
1219eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    && hypotenuse >= mHalfwayHypotenusePoint) {
1220eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mIsOnInnerCircle = false;
1221eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else {
1222eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return -1;
1223eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1224eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
1225eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            final int index =  (mShowHours) ? HOURS : MINUTES;
1226eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            final float length = (mCircleRadius[index] * mNumbersRadiusMultiplier[index]);
1227eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            final int distanceToNumber = (int) Math.abs(hypotenuse - length);
1228eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            final int maxAllowedDistance =
1229eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    (int) (mCircleRadius[index] * (1 - mNumbersRadiusMultiplier[index]));
1230eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (distanceToNumber > maxAllowedDistance) {
1231eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return -1;
1232eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1233eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1234eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1235eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final float opposite = Math.abs(y - mYCenter);
1236eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        double degrees = Math.toDegrees(Math.asin(opposite / hypotenuse));
1237eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1238eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Now we have to translate to the correct quadrant.
1239eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        boolean rightSide = (x > mXCenter);
1240eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        boolean topSide = (y < mYCenter);
1241eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (rightSide && topSide) {
1242eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            degrees = 90 - degrees;
1243eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (rightSide && !topSide) {
1244eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            degrees = 90 + degrees;
1245eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (!rightSide && !topSide) {
1246eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            degrees = 270 - degrees;
1247eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (!rightSide && topSide) {
1248eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            degrees = 270 + degrees;
1249eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1250eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return (int) degrees;
1251eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1252eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1253eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int getIsTouchingAmOrPm(float x, float y) {
1254eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final boolean isLayoutRtl = isLayoutRtl();
1255eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int squaredYDistance = (int) ((y - mAmPmYCenter) * (y - mAmPmYCenter));
1256eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1257eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int distanceToAmCenter = (int) Math.sqrt(
1258eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                (x - mLeftIndicatorXCenter) * (x - mLeftIndicatorXCenter) + squaredYDistance);
1259eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (distanceToAmCenter <= mAmPmCircleRadius) {
1260eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return (isLayoutRtl ? PM : AM);
1261eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1262eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1263eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int distanceToPmCenter = (int) Math.sqrt(
1264eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                (x - mRightIndicatorXCenter) * (x - mRightIndicatorXCenter) + squaredYDistance);
1265eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (distanceToPmCenter <= mAmPmCircleRadius) {
1266eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return (isLayoutRtl ? AM : PM);
1267eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1268eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1269eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Neither was close enough.
1270eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return -1;
1271eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1272eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1273eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
1274eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public boolean onTouch(View v, MotionEvent event) {
1275eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if(!mInputEnabled) {
1276eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return true;
1277eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1278eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1279eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final float eventX = event.getX();
1280eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final float eventY = event.getY();
1281eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1282eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int degrees;
1283eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int snapDegrees;
1284eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        boolean result = false;
1285eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1286eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        switch(event.getAction()) {
1287eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case MotionEvent.ACTION_DOWN:
1288eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case MotionEvent.ACTION_MOVE:
1289eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mAmOrPmPressed = getIsTouchingAmOrPm(eventX, eventY);
1290eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (mAmOrPmPressed != -1) {
1291eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    result = true;
1292eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                } else {
1293eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    degrees = getDegreesFromXY(eventX, eventY);
1294eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    if (degrees != -1) {
1295eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        snapDegrees = (mShowHours ?
1296eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                                snapOnly30s(degrees, 0) : snapPrefer30s(degrees)) % 360;
1297eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        if (mShowHours) {
1298eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            mSelectionDegrees[HOURS] = snapDegrees;
1299eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            mSelectionDegrees[HOURS_INNER] = snapDegrees;
1300eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        } else {
1301eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            mSelectionDegrees[MINUTES] = snapDegrees;
1302eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        }
1303eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
1304eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        if (mListener != null) {
1305eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            if (mShowHours) {
1306eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                                mListener.onValueSelected(HOURS, getCurrentHour(), false);
1307eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            } else  {
1308eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                                mListener.onValueSelected(MINUTES, getCurrentMinute(), false);
1309eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            }
1310eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        }
1311eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        result = true;
1312eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    }
1313eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1314eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                invalidate();
1315eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return result;
1316eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1317eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case MotionEvent.ACTION_UP:
1318eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mAmOrPmPressed = getIsTouchingAmOrPm(eventX, eventY);
1319eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (mAmOrPmPressed != -1) {
1320eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    if (mAmOrPm != mAmOrPmPressed) {
1321eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        swapAmPm();
1322eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    }
1323eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    mAmOrPmPressed = -1;
1324eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    if (mListener != null) {
1325eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        mListener.onValueSelected(AMPM, getCurrentHour(), true);
1326eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    }
1327eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    result = true;
1328eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                } else {
1329eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    degrees = getDegreesFromXY(eventX, eventY);
1330eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    if (degrees != -1) {
1331eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        snapDegrees = (mShowHours ?
1332eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                                snapOnly30s(degrees, 0) : snapPrefer30s(degrees)) % 360;
1333eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        if (mShowHours) {
1334eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            mSelectionDegrees[HOURS] = snapDegrees;
1335eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            mSelectionDegrees[HOURS_INNER] = snapDegrees;
1336eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        } else {
1337eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            mSelectionDegrees[MINUTES] = snapDegrees;
1338eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        }
1339eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        if (mListener != null) {
1340eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            if (mShowHours) {
1341eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                                mListener.onValueSelected(HOURS, getCurrentHour(), true);
1342eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            } else  {
1343eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                                mListener.onValueSelected(MINUTES, getCurrentMinute(), true);
1344eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            }
1345eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        }
1346eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        result = true;
1347eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    }
1348eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1349eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (result) {
1350eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    invalidate();
1351eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1352eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return result;
1353eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1354eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            default:
1355eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                break;
1356eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1357eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return false;
1358eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1359eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1360eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
1361eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Necessary for accessibility, to ensure we support "scrolling" forward and backward
1362eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * in the circle.
1363eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
1364eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
1365eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1366eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        super.onInitializeAccessibilityNodeInfo(info);
1367eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
1368eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
1369eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1370eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1371eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
1372eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Announce the currently-selected time when launched.
1373eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
1374eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
1375eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1376eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
1377eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // Clear the event's current text so that only the current time will be spoken.
1378eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            event.getText().clear();
1379eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Time time = new Time();
1380eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            time.hour = getCurrentHour();
1381eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            time.minute = getCurrentMinute();
1382eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            long millis = time.normalize(true);
1383eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int flags = DateUtils.FORMAT_SHOW_TIME;
1384eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mIs24HourMode) {
1385eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                flags |= DateUtils.FORMAT_24HOUR;
1386eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1387eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            String timeString = DateUtils.formatDateTime(getContext(), millis, flags);
1388eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            event.getText().add(timeString);
1389eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return true;
1390eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1391eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return super.dispatchPopulateAccessibilityEvent(event);
1392eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1393eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1394eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
1395eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * When scroll forward/backward events are received, jump the time to the higher/lower
1396eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * discrete, visible value on the circle.
1397eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
1398eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @SuppressLint("NewApi")
1399eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
1400eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public boolean performAccessibilityAction(int action, Bundle arguments) {
1401eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (super.performAccessibilityAction(action, arguments)) {
1402eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return true;
1403eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1404eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1405eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int changeMultiplier = 0;
1406eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) {
1407eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            changeMultiplier = 1;
1408eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
1409eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            changeMultiplier = -1;
1410eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1411eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (changeMultiplier != 0) {
1412eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int value = 0;
1413eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int stepSize = 0;
1414eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mShowHours) {
1415eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                stepSize = DEGREES_FOR_ONE_HOUR;
1416eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                value = getCurrentHour() % 12;
1417eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else {
1418eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                stepSize = DEGREES_FOR_ONE_MINUTE;
1419eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                value = getCurrentMinute();
1420eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1421eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1422eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int degrees = value * stepSize;
1423eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            degrees = snapOnly30s(degrees, changeMultiplier);
1424eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            value = degrees / stepSize;
1425eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int maxValue = 0;
1426eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int minValue = 0;
1427eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mShowHours) {
1428eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (mIs24HourMode) {
1429eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    maxValue = 23;
1430eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                } else {
1431eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    maxValue = 12;
1432eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    minValue = 1;
1433eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1434eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else {
1435eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                maxValue = 55;
1436eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1437eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (value > maxValue) {
1438eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                // If we scrolled forward past the highest number, wrap around to the lowest.
1439eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                value = minValue;
1440eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else if (value < minValue) {
1441eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                // If we scrolled backward past the lowest number, wrap around to the highest.
1442eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                value = maxValue;
1443eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1444eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mShowHours) {
1445eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                setCurrentHour(value);
1446eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (mListener != null) {
1447eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    mListener.onValueSelected(HOURS, value, false);
1448eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1449eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else {
1450eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                setCurrentMinute(value);
1451eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (mListener != null) {
1452eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    mListener.onValueSelected(MINUTES, value, false);
1453eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1454eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1455eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return true;
1456eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1457eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1458eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return false;
1459eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1460eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1461eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setInputEnabled(boolean inputEnabled) {
1462eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInputEnabled = inputEnabled;
1463eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        invalidate();
1464eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1465eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette
1466eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette    private static class IntHolder {
1467eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        private int mValue;
1468eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette
1469eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        public IntHolder(int value) {
1470eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mValue = value;
1471eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        }
1472eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette
1473eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        public void setValue(int value) {
1474eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            mValue = value;
1475eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        }
1476eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette
1477eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        public int getValue() {
1478eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette            return mValue;
1479eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette        }
1480eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette    }
1481eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio}
1482