RadialTimePickerView.java revision d8c2af55420d869c140278e6643d16cd90cfeb51
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.content.Context; 26ec9fe1ad663d59829075029508673b2e913538d3Alan Viveretteimport android.content.res.ColorStateList; 27eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.content.res.Resources; 28eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.content.res.TypedArray; 29eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.graphics.Canvas; 30eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.graphics.Color; 31eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.graphics.Paint; 32ec9fe1ad663d59829075029508673b2e913538d3Alan Viveretteimport android.graphics.Path; 33ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viveretteimport android.graphics.Rect; 34ec9fe1ad663d59829075029508673b2e913538d3Alan Viveretteimport android.graphics.Region; 35eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.graphics.Typeface; 36eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.os.Bundle; 37eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.util.AttributeSet; 38ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viveretteimport android.util.IntArray; 39eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.util.Log; 40ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viveretteimport android.util.MathUtils; 41ec9fe1ad663d59829075029508673b2e913538d3Alan Viveretteimport android.util.StateSet; 425134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viveretteimport android.util.TypedValue; 43eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.HapticFeedbackConstants; 44eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.MotionEvent; 45eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.View; 46eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.ViewGroup; 47eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.accessibility.AccessibilityEvent; 48eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.accessibility.AccessibilityNodeInfo; 49ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viveretteimport android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 50eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette 51eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport com.android.internal.R; 52ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viveretteimport com.android.internal.widget.ExploreByTouchHelper; 53eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 54eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport java.util.ArrayList; 55eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport java.util.Calendar; 56eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport java.util.Locale; 57eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 58eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio/** 59eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * View to show a clock circle picker (with one or two picking circles) 60eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * 61eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * @hide 62eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio */ 63ec9fe1ad663d59829075029508673b2e913538d3Alan Viverettepublic class RadialTimePickerView extends View { 64ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private static final String TAG = "RadialTimePickerView"; 65eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 66eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final boolean DEBUG = false; 67eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 68eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int DEBUG_COLOR = 0x20FF0000; 69eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int DEBUG_TEXT_COLOR = 0x60FF0000; 70eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int DEBUG_STROKE_WIDTH = 2; 71eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 72eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int HOURS = 0; 73eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int MINUTES = 1; 74eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int HOURS_INNER = 2; 75eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 76eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int SELECTOR_CIRCLE = 0; 77eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int SELECTOR_DOT = 1; 78eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int SELECTOR_LINE = 2; 79eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 80eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int AM = 0; 81eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int PM = 1; 82eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 83eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Opaque alpha level 84eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int ALPHA_OPAQUE = 255; 85eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 86eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Transparent alpha level 87eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int ALPHA_TRANSPARENT = 0; 88eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 89eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int DEGREES_FOR_ONE_HOUR = 30; 90eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int DEGREES_FOR_ONE_MINUTE = 6; 91eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 92eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int[] HOURS_NUMBERS = {12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; 93eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int[] HOURS_NUMBERS_24 = {0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; 94eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int[] MINUTES_NUMBERS = {0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55}; 95eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 96eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static final int CENTER_RADIUS = 2; 97eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 98ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private static final int FADE_OUT_DURATION = 500; 99ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private static final int FADE_IN_DURATION = 500; 100ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 101ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private static final int[] SNAP_PREFER_30S_MAP = new int[361]; 102ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 103ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private static final int NUM_POSITIONS = 12; 104ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private static final float[] COS_30 = new float[NUM_POSITIONS]; 105ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private static final float[] SIN_30 = new float[NUM_POSITIONS]; 106ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 107ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette static { 108ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette // Prepare mapping to snap touchable degrees to selectable degrees. 109ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette preparePrefer30sMap(); 110ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 111ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final double increment = 2.0 * Math.PI / NUM_POSITIONS; 112ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette double angle = Math.PI / 2.0; 113ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette for (int i = 0; i < NUM_POSITIONS; i++) { 114ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette COS_30[i] = (float) Math.cos(angle); 115ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette SIN_30[i] = (float) Math.sin(angle); 116ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette angle += increment; 117ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette } 118ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette } 119eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 120ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final InvalidateUpdateListener mInvalidateUpdateListener = 121ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette new InvalidateUpdateListener(); 122ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 123eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private final String[] mHours12Texts = new String[12]; 124eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private final String[] mOuterHours24Texts = new String[12]; 125eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private final String[] mInnerHours24Texts = new String[12]; 126eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private final String[] mMinutesTexts = new String[12]; 127eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 128eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private final Paint[] mPaint = new Paint[2]; 129eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette private final IntHolder[] mAlpha = new IntHolder[2]; 130eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette 131eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private final Paint mPaintCenter = new Paint(); 132eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette 133eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private final Paint[][] mPaintSelector = new Paint[2][3]; 134eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette private final int[][] mColorSelector = new int[2][3]; 135eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette private final IntHolder[][] mAlphaSelector = new IntHolder[2][3]; 136eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette 137eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private final Paint mPaintBackground = new Paint(); 138eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private final Paint mPaintDebug = new Paint(); 139eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 140ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final Typeface mTypeface; 141ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 142ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final float[] mTextSize = new float[2]; 143ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 144ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private final float[][] mOuterTextX = new float[2][12]; 145ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private final float[][] mOuterTextY = new float[2][12]; 146ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 147ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private final float[] mInnerTextX = new float[12]; 148ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private final float[] mInnerTextY = new float[12]; 149ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 150ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final float[] mNumbersRadiusMultiplier = new float[3]; 151ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 152ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final float[] mTextSizeMultiplier = new float[3]; 153ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 154ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final int[] mLineLength = new int[3]; 155ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final int[] mSelectionRadius = new int[3]; 156ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final float mSelectionRadiusMultiplier; 157ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final int[] mSelectionDegrees = new int[3]; 158ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 159ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final ArrayList<Animator> mHoursToMinutesAnims = new ArrayList<Animator>(); 160ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final ArrayList<Animator> mMinuteToHoursAnims = new ArrayList<Animator>(); 161ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 162ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final RadialPickerTouchHelper mTouchHelper; 163ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 164ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private ColorStateList mNumbersTextColor; 165eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 166eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private boolean mIs24HourMode; 167eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private boolean mShowHours; 168d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette 169d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette /** 170d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette * When in 24-hour mode, indicates that the current hour is between 171d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette * 1 and 12 (inclusive). 172d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette */ 173eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private boolean mIsOnInnerCircle; 174eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 175ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private float mXCenter; 176ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private float mYCenter; 177ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private float mCircleRadius; 178eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 179eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private int mMinHypotenuseForInnerNumber; 180eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private int mMaxHypotenuseForOuterNumber; 181eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private int mHalfwayHypotenusePoint; 182eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 183eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private String[] mOuterTextHours; 184eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private String[] mInnerTextHours; 185eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private String[] mOuterTextMinutes; 186eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private AnimatorSet mTransition; 187eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 188eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private int mAmOrPm; 1895134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette private int mDisabledAlpha; 1905134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette 191eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private OnValueSelectedListener mListener; 192eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 193ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private boolean mInputEnabled = true; 194eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 195eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public interface OnValueSelectedListener { 196eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance); 197eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 198eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 199eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio /** 200eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * Split up the 360 degrees of the circle among the 60 selectable values. Assigns a larger 201eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * selectable area to each of the 12 visible values, such that the ratio of space apportioned 202eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * to a visible value : space apportioned to a non-visible value will be 14 : 4. 203eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * E.g. the output of 30 degrees should have a higher range of input associated with it than 204eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * the output of 24 degrees, because 30 degrees corresponds to a visible number on the clock 205eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * circle (5 on the minutes, 1 or 13 on the hours). 206eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio */ 207eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static void preparePrefer30sMap() { 208eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // We'll split up the visible output and the non-visible output such that each visible 209eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // output will correspond to a range of 14 associated input degrees, and each non-visible 210eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // output will correspond to a range of 4 associate input degrees, so visible numbers 211eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // are more than 3 times easier to get than non-visible numbers: 212eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // {354-359,0-7}:0, {8-11}:6, {12-15}:12, {16-19}:18, {20-23}:24, {24-37}:30, etc. 213eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // 214eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // If an output of 30 degrees should correspond to a range of 14 associated degrees, then 215eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // we'll need any input between 24 - 37 to snap to 30. Working out from there, 20-23 should 216eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // snap to 24, while 38-41 should snap to 36. This is somewhat counter-intuitive, that you 217eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // can be touching 36 degrees but have the selection snapped to 30 degrees; however, this 218eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // inconsistency isn't noticeable at such fine-grained degrees, and it affords us the 219eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // ability to aggressively prefer the visible values by a factor of more than 3:1, which 220eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // greatly contributes to the selectability of these values. 221eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 222eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // The first output is 0, and each following output will increment by 6 {0, 6, 12, ...}. 223eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio int snappedOutputDegrees = 0; 224eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Count of how many inputs we've designated to the specified output. 225eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio int count = 1; 226eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // How many input we expect for a specified output. This will be 14 for output divisible 227eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // by 30, and 4 for the remaining output. We'll special case the outputs of 0 and 360, so 228eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // the caller can decide which they need. 229eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio int expectedCount = 8; 230eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Iterate through the input. 231eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio for (int degrees = 0; degrees < 361; degrees++) { 232eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Save the input-output mapping. 233ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette SNAP_PREFER_30S_MAP[degrees] = snappedOutputDegrees; 234eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // If this is the last input for the specified output, calculate the next output and 235eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // the next expected count. 236eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (count == expectedCount) { 237eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio snappedOutputDegrees += 6; 238eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (snappedOutputDegrees == 360) { 239eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio expectedCount = 7; 240eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } else if (snappedOutputDegrees % 30 == 0) { 241eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio expectedCount = 14; 242eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } else { 243eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio expectedCount = 4; 244eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 245eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio count = 1; 246eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } else { 247eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio count++; 248eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 249eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 250eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 251eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 252eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio /** 253eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * Returns mapping of any input degrees (0 to 360) to one of 60 selectable output degrees, 254eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * where the degrees corresponding to visible numbers (i.e. those divisible by 30) will be 255eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * weighted heavier than the degrees corresponding to non-visible numbers. 256eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * See {@link #preparePrefer30sMap()} documentation for the rationale and generation of the 257eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * mapping. 258eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio */ 259eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static int snapPrefer30s(int degrees) { 260ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette if (SNAP_PREFER_30S_MAP == null) { 261eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio return -1; 262eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 263ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette return SNAP_PREFER_30S_MAP[degrees]; 264eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 265eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 266eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio /** 267eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * Returns mapping of any input degrees (0 to 360) to one of 12 visible output degrees (all 268eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * multiples of 30), where the input will be "snapped" to the closest visible degrees. 269eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * @param degrees The input degrees 270eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * @param forceHigherOrLower The output may be forced to either the higher or lower step, or may 271eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * be allowed to snap to whichever is closer. Use 1 to force strictly higher, -1 to force 272eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * strictly lower, and 0 to snap to the closer one. 273eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * @return output degrees, will be a multiple of 30 274eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio */ 275eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private static int snapOnly30s(int degrees, int forceHigherOrLower) { 276eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio final int stepSize = DEGREES_FOR_ONE_HOUR; 277eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio int floor = (degrees / stepSize) * stepSize; 278eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio final int ceiling = floor + stepSize; 279eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (forceHigherOrLower == 1) { 280eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio degrees = ceiling; 281eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } else if (forceHigherOrLower == -1) { 282eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (degrees == floor) { 283eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio floor -= stepSize; 284eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 285eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio degrees = floor; 286eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } else { 287eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if ((degrees - floor) < (ceiling - degrees)) { 288eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio degrees = floor; 289eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } else { 290eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio degrees = ceiling; 291eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 292eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 293eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio return degrees; 294eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 295eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 296ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette @SuppressWarnings("unused") 297ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette public RadialTimePickerView(Context context) { 298ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette this(context, null); 299ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 300ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 301eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public RadialTimePickerView(Context context, AttributeSet attrs) { 302eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio this(context, attrs, R.attr.timePickerStyle); 303eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 304eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 305ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette public RadialTimePickerView(Context context, AttributeSet attrs, int defStyleAttr) { 306ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette this(context, attrs, defStyleAttr, 0); 307ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 308ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 309ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette public RadialTimePickerView( 310ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 311eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio super(context, attrs); 312eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 3135134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette // Pull disabled alpha from theme. 3145134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette final TypedValue outValue = new TypedValue(); 3155134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true); 3165134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette mDisabledAlpha = (int) (outValue.getFloat() * 255 + 0.5f); 3175134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette 318eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // process style attributes 3195134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette final Resources res = getResources(); 320eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio final TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.TimePicker, 321ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette defStyleAttr, defStyleRes); 322eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 323eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mTypeface = Typeface.create("sans-serif", Typeface.NORMAL); 324eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 325eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette // Initialize all alpha values to opaque. 326eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette for (int i = 0; i < mAlpha.length; i++) { 327eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mAlpha[i] = new IntHolder(ALPHA_OPAQUE); 328eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette } 329eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette for (int i = 0; i < mAlphaSelector.length; i++) { 330eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette for (int j = 0; j < mAlphaSelector[i].length; j++) { 331eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mAlphaSelector[i][j] = new IntHolder(ALPHA_OPAQUE); 332eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette } 333eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette } 334eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette 335ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mNumbersTextColor = a.getColorStateList( 336ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette R.styleable.TimePicker_numbersTextColor); 337eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette 338eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaint[HOURS] = new Paint(); 339eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaint[HOURS].setAntiAlias(true); 340eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaint[HOURS].setTextAlign(Paint.Align.CENTER); 341eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 342eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaint[MINUTES] = new Paint(); 343eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaint[MINUTES].setAntiAlias(true); 344eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaint[MINUTES].setTextAlign(Paint.Align.CENTER); 345eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 346ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final ColorStateList selectorColors = a.getColorStateList( 347ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette R.styleable.TimePicker_numbersSelectorColor); 348ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final int selectorActivatedColor = selectorColors.getColorForState( 349ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0); 350ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 351ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mPaintCenter.setColor(selectorActivatedColor); 352eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintCenter.setAntiAlias(true); 353ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 354ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final int textActivatedColor = mNumbersTextColor.getColorForState( 355ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0); 356eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 357eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintSelector[HOURS][SELECTOR_CIRCLE] = new Paint(); 358eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintSelector[HOURS][SELECTOR_CIRCLE].setAntiAlias(true); 359ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mColorSelector[HOURS][SELECTOR_CIRCLE] = selectorActivatedColor; 360eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 361eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintSelector[HOURS][SELECTOR_DOT] = new Paint(); 362eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintSelector[HOURS][SELECTOR_DOT].setAntiAlias(true); 363ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mColorSelector[HOURS][SELECTOR_DOT] = textActivatedColor; 364eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 365eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintSelector[HOURS][SELECTOR_LINE] = new Paint(); 366eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintSelector[HOURS][SELECTOR_LINE].setAntiAlias(true); 367eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintSelector[HOURS][SELECTOR_LINE].setStrokeWidth(2); 368ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mColorSelector[HOURS][SELECTOR_LINE] = selectorActivatedColor; 369eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 370eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintSelector[MINUTES][SELECTOR_CIRCLE] = new Paint(); 371eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintSelector[MINUTES][SELECTOR_CIRCLE].setAntiAlias(true); 372ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mColorSelector[MINUTES][SELECTOR_CIRCLE] = selectorActivatedColor; 373eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 374eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintSelector[MINUTES][SELECTOR_DOT] = new Paint(); 375eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintSelector[MINUTES][SELECTOR_DOT].setAntiAlias(true); 376ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mColorSelector[MINUTES][SELECTOR_DOT] = textActivatedColor; 377eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 378eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintSelector[MINUTES][SELECTOR_LINE] = new Paint(); 379eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintSelector[MINUTES][SELECTOR_LINE].setAntiAlias(true); 380eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintSelector[MINUTES][SELECTOR_LINE].setStrokeWidth(2); 381ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mColorSelector[MINUTES][SELECTOR_LINE] = selectorActivatedColor; 382eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 383eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mPaintBackground.setColor(a.getColor(R.styleable.TimePicker_numbersBackgroundColor, 384ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette context.getColor(R.color.timepicker_default_numbers_background_color_material))); 385eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintBackground.setAntiAlias(true); 386eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 387eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (DEBUG) { 388eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintDebug.setColor(DEBUG_COLOR); 389eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintDebug.setAntiAlias(true); 390eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintDebug.setStrokeWidth(DEBUG_STROKE_WIDTH); 391eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintDebug.setStyle(Paint.Style.STROKE); 392eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mPaintDebug.setTextAlign(Paint.Align.CENTER); 393eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 394eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 395eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mShowHours = true; 396eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mIs24HourMode = false; 397eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mAmOrPm = AM; 398eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 399ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette // Set up accessibility components. 400ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette mTouchHelper = new RadialPickerTouchHelper(); 401ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette setAccessibilityDelegate(mTouchHelper); 402ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 403ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 404ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); 405ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 406ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 407eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio initHoursAndMinutesText(); 408eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio initData(); 409eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 410ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mSelectionRadiusMultiplier = res.getFloat(R.dimen.timepicker_selection_radius_multiplier); 411eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 412eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette a.recycle(); 413eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette 414eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Initial values 415eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio final Calendar calendar = Calendar.getInstance(Locale.getDefault()); 416eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio final int currentHour = calendar.get(Calendar.HOUR_OF_DAY); 417eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio final int currentMinute = calendar.get(Calendar.MINUTE); 418eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 419ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette setCurrentHourInternal(currentHour, false, false); 420ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette setCurrentMinuteInternal(currentMinute, false); 421eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 422eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio setHapticFeedbackEnabled(true); 423eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 424eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 425eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public void initialize(int hour, int minute, boolean is24HourMode) { 426448ff718a0bb92c5b760668c12d2449a00f9aa2aAlan Viverette if (mIs24HourMode != is24HourMode) { 427448ff718a0bb92c5b760668c12d2449a00f9aa2aAlan Viverette mIs24HourMode = is24HourMode; 428448ff718a0bb92c5b760668c12d2449a00f9aa2aAlan Viverette initData(); 429448ff718a0bb92c5b760668c12d2449a00f9aa2aAlan Viverette } 430ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 431ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette setCurrentHourInternal(hour, false, false); 432ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette setCurrentMinuteInternal(minute, false); 433eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 434eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 435eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public void setCurrentItemShowing(int item, boolean animate) { 436eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio switch (item){ 437eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio case HOURS: 438eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio showHours(animate); 439eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio break; 440eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio case MINUTES: 441eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio showMinutes(animate); 442eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio break; 443eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio default: 444eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio Log.e(TAG, "ClockView does not support showing item " + item); 445eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 446eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 447eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 448eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public int getCurrentItemShowing() { 449eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio return mShowHours ? HOURS : MINUTES; 450eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 451eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 452eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public void setOnValueSelectedListener(OnValueSelectedListener listener) { 453eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mListener = listener; 454eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 455eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 456d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette /** 457d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette * Sets the current hour in 24-hour time. 458d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette * 459d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette * @param hour the current hour between 0 and 23 (inclusive) 460d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette */ 461eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public void setCurrentHour(int hour) { 462ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette setCurrentHourInternal(hour, true, false); 463ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 464ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 465ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette /** 466ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette * Sets the current hour. 467ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette * 468ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette * @param hour The current hour 469ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette * @param callback Whether the value listener should be invoked 470ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette * @param autoAdvance Whether the listener should auto-advance to the next 471ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette * selection mode, e.g. hour to minutes 472ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette */ 473ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private void setCurrentHourInternal(int hour, boolean callback, boolean autoAdvance) { 474eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio final int degrees = (hour % 12) * DEGREES_FOR_ONE_HOUR; 475eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mSelectionDegrees[HOURS] = degrees; 476eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mSelectionDegrees[HOURS_INNER] = degrees; 477d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette 478d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette // 0 is 12 AM (midnight) and 12 is 12 PM (noon). 479ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int amOrPm = (hour == 0 || (hour % 24) < 12) ? AM : PM; 480ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final boolean isOnInnerCircle = mIs24HourMode && hour >= 1 && hour <= 12; 481ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (mAmOrPm != amOrPm || mIsOnInnerCircle != isOnInnerCircle) { 482ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette mAmOrPm = amOrPm; 483ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette mIsOnInnerCircle = isOnInnerCircle; 484ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 485ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette initData(); 486ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette mTouchHelper.invalidateRoot(); 487ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 488d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette 489eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio invalidate(); 490ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 491ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (callback && mListener != null) { 492ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette mListener.onValueSelected(HOURS, hour, autoAdvance); 493ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 494eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 495eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 496d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette /** 497d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette * Returns the current hour in 24-hour time. 498d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette * 499d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette * @return the current hour between 0 and 23 (inclusive) 500d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette */ 501eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public int getCurrentHour() { 502ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return getHourForDegrees( 503ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette mSelectionDegrees[mIsOnInnerCircle ? HOURS_INNER : HOURS], mIsOnInnerCircle); 504ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 505ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 506ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private int getHourForDegrees(int degrees, boolean innerCircle) { 507ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette int hour = (degrees / DEGREES_FOR_ONE_HOUR) % 12; 508eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (mIs24HourMode) { 509d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette // Convert the 12-hour value into 24-hour time based on where the 510d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette // selector is positioned. 511ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (innerCircle && hour == 0) { 512d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette // Inner circle is 1 through 12. 513d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette hour = 12; 514ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else if (!innerCircle && hour != 0) { 515d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette // Outer circle is 13 through 23 and 0. 516d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette hour += 12; 517eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 518d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette } else if (mAmOrPm == PM) { 519d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette hour += 12; 520eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 521d735c9b1ed36c7a45afc662c109c2dbf3e205e39Alan Viverette return hour; 522eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 523eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 524ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private int getDegreesForHour(int hour) { 525ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette // Convert to be 0-11. 526ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (mIs24HourMode) { 527ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (hour >= 12) { 528ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette hour -= 12; 529ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 530ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else if (hour == 12) { 531ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette hour = 0; 532ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 533ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return hour * DEGREES_FOR_ONE_HOUR; 534ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 535ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 536eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public void setCurrentMinute(int minute) { 537ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette setCurrentMinuteInternal(minute, true); 538ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 539ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 540ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private void setCurrentMinuteInternal(int minute, boolean callback) { 541eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mSelectionDegrees[MINUTES] = (minute % 60) * DEGREES_FOR_ONE_MINUTE; 542ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 543eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio invalidate(); 544ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 545ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (callback && mListener != null) { 546ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette mListener.onValueSelected(MINUTES, minute, false); 547ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 548eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 549eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 550eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Returns minutes in 0-59 range 551eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public int getCurrentMinute() { 552ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return getMinuteForDegrees(mSelectionDegrees[MINUTES]); 553ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 554ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 555ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private int getMinuteForDegrees(int degrees) { 556ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return degrees / DEGREES_FOR_ONE_MINUTE; 557ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 558ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 559ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private int getDegreesForMinute(int minute) { 560ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return minute * DEGREES_FOR_ONE_MINUTE; 561eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 562eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 563eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public void setAmOrPm(int val) { 564eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mAmOrPm = (val % 2); 565eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio invalidate(); 566ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette mTouchHelper.invalidateRoot(); 567eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 568eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 569eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public int getAmOrPm() { 570eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio return mAmOrPm; 571eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 572eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 573eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public void showHours(boolean animate) { 574ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette if (mShowHours) { 575ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette return; 576ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette } 577ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 578eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mShowHours = true; 579ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 580eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (animate) { 581eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio startMinutesToHoursAnimation(); 582eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 583ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 584eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio initData(); 585eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio invalidate(); 586eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 587eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 588eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public void showMinutes(boolean animate) { 589ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette if (!mShowHours) { 590ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette return; 591ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette } 592ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 593eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mShowHours = false; 594ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 595eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (animate) { 596eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio startHoursToMinutesAnimation(); 597eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 598ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 599eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio initData(); 600eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio invalidate(); 601eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 602eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 603eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private void initHoursAndMinutesText() { 604eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Initialize the hours and minutes numbers. 605eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio for (int i = 0; i < 12; i++) { 606eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mHours12Texts[i] = String.format("%d", HOURS_NUMBERS[i]); 607eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mOuterHours24Texts[i] = String.format("%02d", HOURS_NUMBERS_24[i]); 608eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mInnerHours24Texts[i] = String.format("%d", HOURS_NUMBERS[i]); 609eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mMinutesTexts[i] = String.format("%02d", MINUTES_NUMBERS[i]); 610eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 611eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 612eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 613eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private void initData() { 614eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (mIs24HourMode) { 615eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mOuterTextHours = mOuterHours24Texts; 616eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mInnerTextHours = mInnerHours24Texts; 617eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } else { 618eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mOuterTextHours = mHours12Texts; 619eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mInnerTextHours = null; 620eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 621eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 622eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mOuterTextMinutes = mMinutesTexts; 623eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 624eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio final Resources res = getResources(); 625eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 626ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette if (mIs24HourMode) { 627ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mNumbersRadiusMultiplier[HOURS] = res.getFloat( 628ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette R.dimen.timepicker_numbers_radius_multiplier_outer); 629ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mTextSizeMultiplier[HOURS] = res.getFloat( 630ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette R.dimen.timepicker_text_size_multiplier_outer); 631ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 632ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mNumbersRadiusMultiplier[HOURS_INNER] = res.getFloat( 633ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette R.dimen.timepicker_numbers_radius_multiplier_inner); 634ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mTextSizeMultiplier[HOURS_INNER] = res.getFloat( 635ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette R.dimen.timepicker_text_size_multiplier_inner); 636eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } else { 637ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mNumbersRadiusMultiplier[HOURS] = res.getFloat( 638ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette R.dimen.timepicker_numbers_radius_multiplier_normal); 639ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mTextSizeMultiplier[HOURS] = res.getFloat( 640ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette R.dimen.timepicker_text_size_multiplier_normal); 641eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 642eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 643ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mNumbersRadiusMultiplier[MINUTES] = res.getFloat( 644ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette R.dimen.timepicker_numbers_radius_multiplier_normal); 645ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mTextSizeMultiplier[MINUTES] = res.getFloat( 646ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette R.dimen.timepicker_text_size_multiplier_normal); 647ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 648ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final int hoursAlpha = mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT; 649ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mAlpha[HOURS].setValue(hoursAlpha); 650ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mAlphaSelector[HOURS][SELECTOR_CIRCLE].setValue(hoursAlpha); 651ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mAlphaSelector[HOURS][SELECTOR_DOT].setValue(hoursAlpha); 652ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mAlphaSelector[HOURS][SELECTOR_LINE].setValue(hoursAlpha); 653ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 654ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final int minutesAlpha = mShowHours ? ALPHA_TRANSPARENT : ALPHA_OPAQUE; 655ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mAlpha[MINUTES].setValue(minutesAlpha); 656ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mAlphaSelector[MINUTES][SELECTOR_CIRCLE].setValue(minutesAlpha); 657ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mAlphaSelector[MINUTES][SELECTOR_DOT].setValue(minutesAlpha); 658ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mAlphaSelector[MINUTES][SELECTOR_LINE].setValue(minutesAlpha); 659eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 660eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 661eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio @Override 662eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 663ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette if (!changed) { 664ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette return; 665ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette } 666eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 667eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mXCenter = getWidth() / 2; 668eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mYCenter = getHeight() / 2; 669ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mCircleRadius = Math.min(mXCenter, mYCenter); 670eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 671ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mMinHypotenuseForInnerNumber = (int) (mCircleRadius 672eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * mNumbersRadiusMultiplier[HOURS_INNER]) - mSelectionRadius[HOURS]; 673ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mMaxHypotenuseForOuterNumber = (int) (mCircleRadius 674eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * mNumbersRadiusMultiplier[HOURS]) + mSelectionRadius[HOURS]; 675ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mHalfwayHypotenusePoint = (int) (mCircleRadius 676eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * ((mNumbersRadiusMultiplier[HOURS] + mNumbersRadiusMultiplier[HOURS_INNER]) / 2)); 677eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 678ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mTextSize[HOURS] = mCircleRadius * mTextSizeMultiplier[HOURS]; 679ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mTextSize[MINUTES] = mCircleRadius * mTextSizeMultiplier[MINUTES]; 680eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 681eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (mIs24HourMode) { 682ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mTextSize[HOURS_INNER] = mCircleRadius * mTextSizeMultiplier[HOURS_INNER]; 683eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 684eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 685ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette calculatePositionsHours(); 686ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette calculatePositionsMinutes(); 687eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 688ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mSelectionRadius[HOURS] = (int) (mCircleRadius * mSelectionRadiusMultiplier); 689eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mSelectionRadius[HOURS_INNER] = mSelectionRadius[HOURS]; 690ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mSelectionRadius[MINUTES] = (int) (mCircleRadius * mSelectionRadiusMultiplier); 691ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 692ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette mTouchHelper.invalidateRoot(); 693eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 694eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 695eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio @Override 696eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public void onDraw(Canvas canvas) { 6975134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette if (!mInputEnabled) { 6985134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), mDisabledAlpha); 6995134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette } else { 7005134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette canvas.save(); 7015134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette } 702eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 703eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio drawCircleBackground(canvas); 704eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 705ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final int hoursAlpha = mAlpha[HOURS].getValue(); 706ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette if (hoursAlpha > 0) { 707ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette // Draw the hour selector under the elements. 708ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette drawSelector(canvas, mIsOnInnerCircle ? HOURS_INNER : HOURS, null); 709ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 710ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette // Draw outer hours. 711ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette drawTextElements(canvas, mTextSize[HOURS], mTypeface, mOuterTextHours, 712ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mOuterTextX[HOURS], mOuterTextY[HOURS], mPaint[HOURS], hoursAlpha, 713ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette !mIsOnInnerCircle, mSelectionDegrees[HOURS], false); 714ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 715ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette // Draw inner hours (12-23) for 24-hour time. 716ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette if (mIs24HourMode && mInnerTextHours != null) { 717ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette drawTextElements(canvas, mTextSize[HOURS_INNER], mTypeface, mInnerTextHours, 718ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mInnerTextX, mInnerTextY, mPaint[HOURS], hoursAlpha, 719ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mIsOnInnerCircle, mSelectionDegrees[HOURS], false); 720ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette } 721eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 722eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 723ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final int minutesAlpha = mAlpha[MINUTES].getValue(); 724ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette if (minutesAlpha > 0) { 725ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette drawSelector(canvas, MINUTES, mSelectorPath); 726ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 727ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette // Exclude the selector region, then draw minutes with no 728ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette // activated states. 729ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette canvas.save(Canvas.CLIP_SAVE_FLAG); 730ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette canvas.clipPath(mSelectorPath, Region.Op.DIFFERENCE); 731ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mOuterTextMinutes, 732ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES], minutesAlpha, 733ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette false, 0, false); 734ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette canvas.restore(); 735ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 736ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette // Intersect the selector region, then draw minutes with only 737ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette // activated states. 738ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette canvas.save(Canvas.CLIP_SAVE_FLAG); 739ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette canvas.clipPath(mSelectorPath, Region.Op.INTERSECT); 740ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mOuterTextMinutes, 741ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES], minutesAlpha, 742ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette true, mSelectionDegrees[MINUTES], true); 743ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette canvas.restore(); 744ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette } 745eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 746eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio drawCenter(canvas); 747eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 748eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (DEBUG) { 749eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio drawDebug(canvas); 750eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 751eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 752eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio canvas.restore(); 753eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 754eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 755eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private void drawCircleBackground(Canvas canvas) { 756ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette canvas.drawCircle(mXCenter, mYCenter, mCircleRadius, mPaintBackground); 757eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 758eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 759eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private void drawCenter(Canvas canvas) { 760eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio canvas.drawCircle(mXCenter, mYCenter, CENTER_RADIUS, mPaintCenter); 761eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 762eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 763eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette private int getMultipliedAlpha(int argb, int alpha) { 764eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette return (int) (Color.alpha(argb) * (alpha / 255.0) + 0.5); 765eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette } 766eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette 767ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private final Path mSelectorPath = new Path(); 768ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 769ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private void drawSelector(Canvas canvas, int index, Path selectorPath) { 770eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Calculate the current radius at which to place the selection circle. 771ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mLineLength[index] = (int) (mCircleRadius * mNumbersRadiusMultiplier[index]); 772eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 773ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final double selectionRadians = Math.toRadians(mSelectionDegrees[index]); 774eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 775ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette float pointX = mXCenter + (int) (mLineLength[index] * Math.sin(selectionRadians)); 776ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette float pointY = mYCenter - (int) (mLineLength[index] * Math.cos(selectionRadians)); 777eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 778eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette int color; 779eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette int alpha; 780eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette Paint paint; 781eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette 782eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Draw the selection circle 783eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette color = mColorSelector[index % 2][SELECTOR_CIRCLE]; 784eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette alpha = mAlphaSelector[index % 2][SELECTOR_CIRCLE].getValue(); 785eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette paint = mPaintSelector[index % 2][SELECTOR_CIRCLE]; 786eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette paint.setColor(color); 787eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette paint.setAlpha(getMultipliedAlpha(color, alpha)); 788eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette canvas.drawCircle(pointX, pointY, mSelectionRadius[index], paint); 789eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 790ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette // If needed, set up the clip path for later. 791ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette if (selectorPath != null) { 792ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mSelectorPath.reset(); 793ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mSelectorPath.addCircle(pointX, pointY, mSelectionRadius[index], Path.Direction.CCW); 794ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette } 795ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 796ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette // Draw the dot if needed. 797ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final boolean shouldDrawDot = mSelectionDegrees[index] % 30 != 0; 798ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette if (shouldDrawDot) { 799eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // We're not on a direct tick 800eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette color = mColorSelector[index % 2][SELECTOR_DOT]; 801eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette alpha = mAlphaSelector[index % 2][SELECTOR_DOT].getValue(); 802eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette paint = mPaintSelector[index % 2][SELECTOR_DOT]; 803eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette paint.setColor(color); 804eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette paint.setAlpha(getMultipliedAlpha(color, alpha)); 805ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette canvas.drawCircle(pointX, pointY, (mSelectionRadius[index] * 0.125f), paint); 806eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 807eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 808ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette // Shorten the line to only go to the edge of the selection circle. 809ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final int lineLength = mLineLength[index] - mSelectionRadius[index]; 810ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette pointX = mXCenter + (int) (lineLength * Math.sin(selectionRadians)); 811ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette pointY = mYCenter - (int) (lineLength * Math.cos(selectionRadians)); 812ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 813eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Draw the line 814eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette color = mColorSelector[index % 2][SELECTOR_LINE]; 815eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette alpha = mAlphaSelector[index % 2][SELECTOR_LINE].getValue(); 816eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette paint = mPaintSelector[index % 2][SELECTOR_LINE]; 817eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette paint.setColor(color); 818eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette paint.setAlpha(getMultipliedAlpha(color, alpha)); 819eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette canvas.drawLine(mXCenter, mYCenter, pointX, pointY, paint); 820eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 821eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 822eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private void drawDebug(Canvas canvas) { 823eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Draw outer numbers circle 824ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final float outerRadius = mCircleRadius * mNumbersRadiusMultiplier[HOURS]; 825eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio canvas.drawCircle(mXCenter, mYCenter, outerRadius, mPaintDebug); 826eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 827eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Draw inner numbers circle 828ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final float innerRadius = mCircleRadius * mNumbersRadiusMultiplier[HOURS_INNER]; 829eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio canvas.drawCircle(mXCenter, mYCenter, innerRadius, mPaintDebug); 830eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 831eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Draw outer background circle 832ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette canvas.drawCircle(mXCenter, mYCenter, mCircleRadius, mPaintDebug); 833eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 834eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Draw outer rectangle for circles 835eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio float left = mXCenter - outerRadius; 836eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio float top = mYCenter - outerRadius; 837eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio float right = mXCenter + outerRadius; 838eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio float bottom = mYCenter + outerRadius; 839ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette canvas.drawRect(left, top, right, bottom, mPaintDebug); 840eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 841eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Draw outer rectangle for background 842ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette left = mXCenter - mCircleRadius; 843ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette top = mYCenter - mCircleRadius; 844ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette right = mXCenter + mCircleRadius; 845ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette bottom = mYCenter + mCircleRadius; 846ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette canvas.drawRect(left, top, right, bottom, mPaintDebug); 847eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 848eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Draw outer view rectangle 849ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette canvas.drawRect(0, 0, getWidth(), getHeight(), mPaintDebug); 850eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 851eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Draw selected time 852eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio final String selected = String.format("%02d:%02d", getCurrentHour(), getCurrentMinute()); 853eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 854eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 855eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio ViewGroup.LayoutParams.WRAP_CONTENT); 856eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio TextView tv = new TextView(getContext()); 857eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio tv.setLayoutParams(lp); 858eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio tv.setText(selected); 859eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio tv.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); 860eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio Paint paint = tv.getPaint(); 861eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio paint.setColor(DEBUG_TEXT_COLOR); 862eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 863eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio final int width = tv.getMeasuredWidth(); 864eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 865eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio float height = paint.descent() - paint.ascent(); 866eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio float x = mXCenter - width / 2; 867eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio float y = mYCenter + 1.5f * height; 868eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 86967945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette canvas.drawText(selected, x, y, paint); 870eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 871eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 872ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private void calculatePositionsHours() { 873eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Calculate the text positions 874ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final float numbersRadius = mCircleRadius * mNumbersRadiusMultiplier[HOURS]; 875eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 876eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Calculate the positions for the 12 numbers in the main circle. 877ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette calculatePositions(mPaint[HOURS], numbersRadius, mXCenter, mYCenter, 878ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mTextSize[HOURS], mOuterTextX[HOURS], mOuterTextY[HOURS]); 879eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 880eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // If we have an inner circle, calculate those positions too. 881eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (mIs24HourMode) { 882ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final float innerNumbersRadius = mCircleRadius 883ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette * mNumbersRadiusMultiplier[HOURS_INNER]; 884eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 885ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette calculatePositions(mPaint[HOURS], innerNumbersRadius, mXCenter, mYCenter, 886ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mTextSize[HOURS_INNER], mInnerTextX, mInnerTextY); 887eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 888eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 889eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 890ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private void calculatePositionsMinutes() { 891eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Calculate the text positions 892ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final float numbersRadius = mCircleRadius * mNumbersRadiusMultiplier[MINUTES]; 893eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 894eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Calculate the positions for the 12 numbers in the main circle. 895ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette calculatePositions(mPaint[MINUTES], numbersRadius, mXCenter, mYCenter, 896ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette mTextSize[MINUTES], mOuterTextX[MINUTES], mOuterTextY[MINUTES]); 897eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 898eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 899eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio /** 900eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * Using the trigonometric Unit Circle, calculate the positions that the text will need to be 901eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * drawn at based on the specified circle radius. Place the values in the textGridHeights and 902eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * textGridWidths parameters. 903eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio */ 904ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private static void calculatePositions(Paint paint, float radius, float xCenter, float yCenter, 905ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette float textSize, float[] x, float[] y) { 906ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette // Adjust yCenter to account for the text's baseline. 907eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio paint.setTextSize(textSize); 908eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio yCenter -= (paint.descent() + paint.ascent()) / 2; 909eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 910ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette for (int i = 0; i < NUM_POSITIONS; i++) { 911ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette x[i] = xCenter - radius * COS_30[i]; 912ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette y[i] = yCenter - radius * SIN_30[i]; 913ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette } 914eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 915eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 916eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio /** 917eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * Draw the 12 text values at the positions specified by the textGrid parameters. 918eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio */ 919eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private void drawTextElements(Canvas canvas, float textSize, Typeface typeface, String[] texts, 920ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette float[] textX, float[] textY, Paint paint, int alpha, boolean showActivated, 921ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette int activatedDegrees, boolean activatedOnly) { 922eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio paint.setTextSize(textSize); 923eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio paint.setTypeface(typeface); 924eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 925ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette // The activated index can touch a range of elements. 926ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final float activatedIndex = activatedDegrees / (360.0f / NUM_POSITIONS); 927ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final int activatedFloor = (int) activatedIndex; 928ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final int activatedCeil = ((int) Math.ceil(activatedIndex)) % NUM_POSITIONS; 929eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 930ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette for (int i = 0; i < 12; i++) { 931ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final boolean activated = (activatedFloor == i || activatedCeil == i); 932ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette if (activatedOnly && !activated) { 933ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette continue; 934ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette } 935eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 936ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final int stateMask = StateSet.VIEW_STATE_ENABLED 937ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette | (showActivated && activated ? StateSet.VIEW_STATE_ACTIVATED : 0); 938ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final int color = mNumbersTextColor.getColorForState(StateSet.get(stateMask), 0); 939ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette paint.setColor(color); 940ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette paint.setAlpha(getMultipliedAlpha(color, alpha)); 941eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 942ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette canvas.drawText(texts[i], textX[i], textY[i], paint); 943ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette } 944eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 945eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 946eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette private static ObjectAnimator getFadeOutAnimator(IntHolder target, int startAlpha, int endAlpha, 947eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio InvalidateUpdateListener updateListener) { 948ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final ObjectAnimator animator = ObjectAnimator.ofInt(target, "value", startAlpha, endAlpha); 949ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette animator.setDuration(FADE_OUT_DURATION); 950eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio animator.addUpdateListener(updateListener); 951eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio return animator; 952eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 953eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 954eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette private static ObjectAnimator getFadeInAnimator(IntHolder target, int startAlpha, int endAlpha, 955eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio InvalidateUpdateListener updateListener) { 956ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final float delayMultiplier = 0.25f; 957ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final float transitionDurationMultiplier = 1f; 958ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final float totalDurationMultiplier = transitionDurationMultiplier + delayMultiplier; 959ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final int totalDuration = (int) (FADE_IN_DURATION * totalDurationMultiplier); 960ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final float delayPoint = (delayMultiplier * FADE_IN_DURATION) / totalDuration; 961eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 962ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final Keyframe kf0, kf1, kf2; 963eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio kf0 = Keyframe.ofInt(0f, startAlpha); 964eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio kf1 = Keyframe.ofInt(delayPoint, startAlpha); 965eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio kf2 = Keyframe.ofInt(1f, endAlpha); 966ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final PropertyValuesHolder fadeIn = PropertyValuesHolder.ofKeyframe("value", kf0, kf1, kf2); 967eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 968ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(target, fadeIn); 969ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette animator.setDuration(totalDuration); 970eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio animator.addUpdateListener(updateListener); 971eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio return animator; 972eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 973eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 974eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private class InvalidateUpdateListener implements ValueAnimator.AnimatorUpdateListener { 975eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio @Override 976eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio public void onAnimationUpdate(ValueAnimator animation) { 977eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio RadialTimePickerView.this.invalidate(); 978eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 979eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 980eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 981eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private void startHoursToMinutesAnimation() { 982eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (mHoursToMinutesAnims.size() == 0) { 983eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mHoursToMinutesAnims.add(getFadeOutAnimator(mAlpha[HOURS], 984eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener)); 985eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_CIRCLE], 986ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener)); 987eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_DOT], 988eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener)); 989eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_LINE], 990ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener)); 991eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 992eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mHoursToMinutesAnims.add(getFadeInAnimator(mAlpha[MINUTES], 993eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener)); 994eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_CIRCLE], 995ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener)); 996eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_DOT], 997eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener)); 998eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_LINE], 999ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener)); 1000eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1001eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1002eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (mTransition != null && mTransition.isRunning()) { 1003eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mTransition.end(); 1004eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1005eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mTransition = new AnimatorSet(); 1006eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mTransition.playTogether(mHoursToMinutesAnims); 1007eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mTransition.start(); 1008eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1009eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1010eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio private void startMinutesToHoursAnimation() { 1011eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (mMinuteToHoursAnims.size() == 0) { 1012eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mMinuteToHoursAnims.add(getFadeOutAnimator(mAlpha[MINUTES], 1013eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener)); 1014eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_CIRCLE], 1015ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener)); 1016eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_DOT], 1017eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener)); 1018eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_LINE], 1019ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener)); 1020eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1021eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mMinuteToHoursAnims.add(getFadeInAnimator(mAlpha[HOURS], 1022eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener)); 1023eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_CIRCLE], 1024ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener)); 1025eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_DOT], 1026eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener)); 1027eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_LINE], 1028ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener)); 1029eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1030eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1031eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (mTransition != null && mTransition.isRunning()) { 1032eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mTransition.end(); 1033eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1034eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mTransition = new AnimatorSet(); 1035eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mTransition.playTogether(mMinuteToHoursAnims); 1036eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mTransition.start(); 1037eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1038eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1039ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette private int getDegreesFromXY(float x, float y, boolean constrainOutside) { 1040eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio final double hypotenuse = Math.sqrt( 1041eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio (y - mYCenter) * (y - mYCenter) + (x - mXCenter) * (x - mXCenter)); 1042eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1043eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Basic check if we're outside the range of the disk 1044ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette if (constrainOutside && hypotenuse > mCircleRadius) { 1045eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio return -1; 1046eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1047ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette 1048eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Check 1049eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (mIs24HourMode && mShowHours) { 1050eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (hypotenuse >= mMinHypotenuseForInnerNumber 1051eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio && hypotenuse <= mHalfwayHypotenusePoint) { 1052eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mIsOnInnerCircle = true; 1053ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette } else if ((hypotenuse <= mMaxHypotenuseForOuterNumber || !constrainOutside) 1054eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio && hypotenuse >= mHalfwayHypotenusePoint) { 1055eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio mIsOnInnerCircle = false; 1056eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } else { 1057eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio return -1; 1058eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1059eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } else { 1060eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio final int index = (mShowHours) ? HOURS : MINUTES; 1061ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final float length = (mCircleRadius * mNumbersRadiusMultiplier[index]); 1062ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final int distanceToNumber = (int) (hypotenuse - length); 1063eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio final int maxAllowedDistance = 1064ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette (int) (mCircleRadius * (1 - mNumbersRadiusMultiplier[index])); 1065ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette if (distanceToNumber < -maxAllowedDistance 1066ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette || (constrainOutside && distanceToNumber > maxAllowedDistance)) { 1067eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio return -1; 1068eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1069eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1070eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1071eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio final float opposite = Math.abs(y - mYCenter); 1072ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette int degrees = (int) (Math.toDegrees(Math.asin(opposite / hypotenuse)) + 0.5); 1073eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1074eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio // Now we have to translate to the correct quadrant. 1075ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final boolean rightSide = (x > mXCenter); 1076ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final boolean topSide = (y < mYCenter); 1077ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (rightSide) { 1078ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (topSide) { 1079ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette degrees = 90 - degrees; 1080ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else { 1081ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette degrees = 90 + degrees; 1082ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1083ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else { 1084ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (topSide) { 1085ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette degrees = 270 + degrees; 1086ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else { 1087ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette degrees = 270 - degrees; 1088ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1089eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1090ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return degrees; 1091eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1092eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1093002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette boolean mChangedDuringTouch = false; 1094002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette 1095eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio @Override 1096ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette public boolean onTouchEvent(MotionEvent event) { 1097002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette if (!mInputEnabled) { 1098eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio return true; 1099eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1100eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1101002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette final int action = event.getActionMasked(); 1102002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette if (action == MotionEvent.ACTION_MOVE 1103002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette || action == MotionEvent.ACTION_UP 1104002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette || action == MotionEvent.ACTION_DOWN) { 1105002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette boolean forceSelection = false; 1106002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette boolean autoAdvance = false; 1107002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette 1108002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette if (action == MotionEvent.ACTION_DOWN) { 1109002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette // This is a new event stream, reset whether the value changed. 1110002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette mChangedDuringTouch = false; 1111002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette } else if (action == MotionEvent.ACTION_UP) { 1112002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette autoAdvance = true; 1113002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette 1114002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette // If we saw a down/up pair without the value changing, assume 1115002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette // this is a single-tap selection and force a change. 1116002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette if (!mChangedDuringTouch) { 1117002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette forceSelection = true; 1118eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1119002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette } 1120eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1121002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette mChangedDuringTouch |= handleTouchInput( 1122002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette event.getX(), event.getY(), forceSelection, autoAdvance); 1123eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1124002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette 1125002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette return true; 1126002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette } 1127002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette 1128002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette private boolean handleTouchInput( 1129002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette float x, float y, boolean forceSelection, boolean autoAdvance) { 1130002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette // Calling getDegreesFromXY has side effects, so cache 1131002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette // whether we used to be on the inner circle. 1132002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette final boolean wasOnInnerCircle = mIsOnInnerCircle; 1133ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette final int degrees = getDegreesFromXY(x, y, false); 1134002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette if (degrees == -1) { 1135002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette return false; 1136002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette } 1137002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette 1138002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette final int[] selectionDegrees = mSelectionDegrees; 113911a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette final int type; 114011a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette final int newValue; 114111a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette final boolean valueChanged; 1142002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette 1143002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette if (mShowHours) { 1144002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette final int snapDegrees = snapOnly30s(degrees, 0) % 360; 114511a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette valueChanged = selectionDegrees[HOURS] != snapDegrees 1146002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette || selectionDegrees[HOURS_INNER] != snapDegrees 114711a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette || wasOnInnerCircle != mIsOnInnerCircle; 1148002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette 114911a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette selectionDegrees[HOURS] = snapDegrees; 115011a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette selectionDegrees[HOURS_INNER] = snapDegrees; 115111a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette type = HOURS; 115211a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette newValue = getCurrentHour(); 1153002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette } else { 1154002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette final int snapDegrees = snapPrefer30s(degrees) % 360; 115511a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette valueChanged = selectionDegrees[MINUTES] != snapDegrees; 1156002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette 115711a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette selectionDegrees[MINUTES] = snapDegrees; 115811a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette type = MINUTES; 115911a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette newValue = getCurrentMinute(); 1160002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette } 1161002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette 116211a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette if (valueChanged || forceSelection || autoAdvance) { 116311a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette // Fire the listener even if we just need to auto-advance. 1164002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette if (mListener != null) { 1165002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette mListener.onValueSelected(type, newValue, autoAdvance); 1166002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette } 116711a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette 116811a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette // Only provide feedback if the value actually changed. 116911a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette if (valueChanged || forceSelection) { 117011a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); 117111a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette invalidate(); 117211a68e18039b6bb2b67c5f7a82fa13d60583b2c3Alan Viverette } 1173002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette return true; 1174002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette } 1175002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette 1176002f918ceae416ba5c382de499ccff12e642c7b5Alan Viverette return false; 1177eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1178eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1179eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio @Override 1180ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette public boolean dispatchHoverEvent(MotionEvent event) { 1181ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette // First right-of-refusal goes the touch exploration helper. 1182ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (mTouchHelper.dispatchHoverEvent(event)) { 1183eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio return true; 1184eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1185ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return super.dispatchHoverEvent(event); 1186eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1187eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1188ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette public void setInputEnabled(boolean inputEnabled) { 1189ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette mInputEnabled = inputEnabled; 1190ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette invalidate(); 1191ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1192ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1193ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private class RadialPickerTouchHelper extends ExploreByTouchHelper { 1194ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final Rect mTempRect = new Rect(); 1195ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1196ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final int TYPE_HOUR = 1; 1197ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final int TYPE_MINUTE = 2; 1198ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1199ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final int SHIFT_TYPE = 0; 1200ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final int MASK_TYPE = 0xF; 1201ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1202ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final int SHIFT_VALUE = 8; 1203ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final int MASK_VALUE = 0xFF; 1204ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1205ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette /** Increment in which virtual views are exposed for minutes. */ 1206ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private final int MINUTE_INCREMENT = 5; 1207ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1208ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette public RadialPickerTouchHelper() { 1209ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette super(RadialTimePickerView.this); 1210eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1211eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1212ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette @Override 1213ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { 1214ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette super.onInitializeAccessibilityNodeInfo(host, info); 1215ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1216ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD); 1217ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD); 1218eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1219ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1220ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette @Override 1221ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette public boolean performAccessibilityAction(View host, int action, Bundle arguments) { 1222ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (super.performAccessibilityAction(host, action, arguments)) { 1223ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return true; 1224eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1225eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1226ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette switch (action) { 1227ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: 1228ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette adjustPicker(1); 1229ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return true; 1230ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: 1231ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette adjustPicker(-1); 1232ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return true; 1233ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1234ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1235ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return false; 1236ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1237ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1238ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private void adjustPicker(int step) { 1239ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int stepSize; 1240ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int initialValue; 124167945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette final int maxValue; 1242ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int minValue; 1243eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (mShowHours) { 1244ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette stepSize = DEGREES_FOR_ONE_HOUR; 1245ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette initialValue = getCurrentHour() % 12; 1246ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1247eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (mIs24HourMode) { 1248eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio maxValue = 23; 1249ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette minValue = 0; 1250eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } else { 1251eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio maxValue = 12; 1252eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio minValue = 1; 1253eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1254eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } else { 1255ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette stepSize = DEGREES_FOR_ONE_MINUTE; 1256ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette initialValue = getCurrentMinute(); 1257ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1258eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio maxValue = 55; 1259ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette minValue = 0; 1260eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1261ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1262ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int steppedValue = snapOnly30s(initialValue * stepSize, step) / stepSize; 1263ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int clampedValue = MathUtils.constrain(steppedValue, minValue, maxValue); 1264ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (mShowHours) { 1265ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette setCurrentHour(clampedValue); 1266ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else { 1267ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette setCurrentMinute(clampedValue); 1268ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1269ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1270ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1271ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette @Override 1272ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette protected int getVirtualViewAt(float x, float y) { 1273ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int id; 12745efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette 12755efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette // Calling getDegreesXY() has side-effects, so we need to cache the 12765efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette // current inner circle value and restore after the call. 12775efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette final boolean wasOnInnerCircle = mIsOnInnerCircle; 1278d8c2af55420d869c140278e6643d16cd90cfeb51Alan Viverette final int degrees = getDegreesFromXY(x, y, true); 12795efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette final boolean isOnInnerCircle = mIsOnInnerCircle; 12805efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette mIsOnInnerCircle = wasOnInnerCircle; 12815efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette 1282ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (degrees != -1) { 1283ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int snapDegrees = snapOnly30s(degrees, 0) % 360; 1284ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (mShowHours) { 12855efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette final int hour24 = getHourForDegrees(snapDegrees, isOnInnerCircle); 12865efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette final int hour = mIs24HourMode ? hour24 : hour24To12(hour24); 1287ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette id = makeId(TYPE_HOUR, hour); 1288ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else { 1289ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int current = getCurrentMinute(); 1290ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int touched = getMinuteForDegrees(degrees); 1291ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int snapped = getMinuteForDegrees(snapDegrees); 1292ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1293ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette // If the touched minute is closer to the current minute 1294ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette // than it is to the snapped minute, return current. 1295ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int minute; 1296ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (Math.abs(current - touched) < Math.abs(snapped - touched)) { 1297ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette minute = current; 1298ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else { 1299ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette minute = snapped; 1300ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1301ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette id = makeId(TYPE_MINUTE, minute); 1302ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1303ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else { 1304ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette id = INVALID_ID; 1305eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1306ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1307ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return id; 1308ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1309ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1310ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette @Override 1311ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette protected void getVisibleVirtualViews(IntArray virtualViewIds) { 1312eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio if (mShowHours) { 1313ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int min = mIs24HourMode ? 0 : 1; 1314ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int max = mIs24HourMode ? 23 : 12; 1315ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette for (int i = min; i <= max ; i++) { 1316ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette virtualViewIds.add(makeId(TYPE_HOUR, i)); 1317eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1318eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } else { 1319ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int current = getCurrentMinute(); 1320ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette for (int i = 0; i < 60; i += MINUTE_INCREMENT) { 1321ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette virtualViewIds.add(makeId(TYPE_MINUTE, i)); 1322ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1323ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette // If the current minute falls between two increments, 1324ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette // insert an extra node for it. 1325ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (current > i && current < i + MINUTE_INCREMENT) { 1326ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette virtualViewIds.add(makeId(TYPE_MINUTE, current)); 1327ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1328eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1329eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1330eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1331eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1332ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette @Override 1333ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) { 1334ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette event.setClassName(getClass().getName()); 1335eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio 1336ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int type = getTypeFromId(virtualViewId); 1337ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int value = getValueFromId(virtualViewId); 1338ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final CharSequence description = getVirtualViewDescription(type, value); 1339ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette event.setContentDescription(description); 1340ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1341ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1342ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette @Override 1343ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) { 1344ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette node.setClassName(getClass().getName()); 1345ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette node.addAction(AccessibilityAction.ACTION_CLICK); 1346ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1347ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int type = getTypeFromId(virtualViewId); 1348ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int value = getValueFromId(virtualViewId); 1349ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final CharSequence description = getVirtualViewDescription(type, value); 1350ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette node.setContentDescription(description); 1351ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1352ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette getBoundsForVirtualView(virtualViewId, mTempRect); 1353ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette node.setBoundsInParent(mTempRect); 1354ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1355ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final boolean selected = isVirtualViewSelected(type, value); 1356ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette node.setSelected(selected); 13573fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette 13583fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette final int nextId = getVirtualViewIdAfter(type, value); 13593fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette if (nextId != INVALID_ID) { 13603fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette node.setTraversalBefore(RadialTimePickerView.this, nextId); 13613fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette } 13623fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette } 13633fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette 13643fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette private int getVirtualViewIdAfter(int type, int value) { 13653fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette if (type == TYPE_HOUR) { 13663fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette final int nextValue = value + 1; 13673fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette final int max = mIs24HourMode ? 23 : 12; 13683fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette if (nextValue <= max) { 13693fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette return makeId(type, nextValue); 13703fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette } 13713fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette } else if (type == TYPE_MINUTE) { 13723fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette final int current = getCurrentMinute(); 13733fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette final int snapValue = value - (value % MINUTE_INCREMENT); 13743fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette final int nextValue = snapValue + MINUTE_INCREMENT; 13753fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette if (value < current && nextValue > current) { 13763fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette // The current value is between two snap values. 13773fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette return makeId(type, current); 13783fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette } else if (nextValue < 60) { 13793fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette return makeId(type, nextValue); 13803fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette } 13813fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette } 13823fc00e3139706c2c90f8e7261eef48086887dc11Alan Viverette return INVALID_ID; 1383ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1384ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1385ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette @Override 1386ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette protected boolean onPerformActionForVirtualView(int virtualViewId, int action, 1387ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette Bundle arguments) { 1388ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (action == AccessibilityNodeInfo.ACTION_CLICK) { 1389ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int type = getTypeFromId(virtualViewId); 1390ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int value = getValueFromId(virtualViewId); 1391ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (type == TYPE_HOUR) { 1392ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int hour = mIs24HourMode ? value : hour12To24(value, mAmOrPm); 1393ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette setCurrentHour(hour); 1394ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return true; 1395ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else if (type == TYPE_MINUTE) { 1396ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette setCurrentMinute(value); 1397ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return true; 1398ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1399ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1400ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return false; 1401ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1402ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1403ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private int hour12To24(int hour12, int amOrPm) { 1404ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette int hour24 = hour12; 1405ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (hour12 == 12) { 1406ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (amOrPm == AM) { 1407ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette hour24 = 0; 1408ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1409ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else if (amOrPm == PM) { 1410ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette hour24 += 12; 1411ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1412ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return hour24; 1413ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1414ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 14155efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette private int hour24To12(int hour24) { 14165efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette if (hour24 == 0) { 14175efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette return 12; 14185efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette } else if (hour24 > 12) { 14195efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette return hour24 - 12; 14205efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette } else { 14215efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette return hour24; 14225efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette } 14235efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette } 14245efe0d199e9a6133af355ace6e4b52b82a65af6eAlan Viverette 1425ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private void getBoundsForVirtualView(int virtualViewId, Rect bounds) { 1426ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final float radius; 1427ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int type = getTypeFromId(virtualViewId); 1428ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final int value = getValueFromId(virtualViewId); 1429ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final float centerRadius; 1430ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final float degrees; 1431ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (type == TYPE_HOUR) { 1432ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final boolean innerCircle = mIs24HourMode && value > 0 && value <= 12; 1433ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (innerCircle) { 1434ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette centerRadius = mCircleRadius * mNumbersRadiusMultiplier[HOURS_INNER]; 1435ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette radius = mSelectionRadius[HOURS_INNER]; 1436ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else { 1437ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette centerRadius = mCircleRadius * mNumbersRadiusMultiplier[HOURS]; 1438ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette radius = mSelectionRadius[HOURS]; 1439ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1440ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1441ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette degrees = getDegreesForHour(value); 1442ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else if (type == TYPE_MINUTE) { 1443ec9fe1ad663d59829075029508673b2e913538d3Alan Viverette centerRadius = mCircleRadius * mNumbersRadiusMultiplier[MINUTES]; 1444ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette degrees = getDegreesForMinute(value); 1445ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette radius = mSelectionRadius[MINUTES]; 1446ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else { 1447ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette // This should never happen. 1448ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette centerRadius = 0; 1449ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette degrees = 0; 1450ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette radius = 0; 1451ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1452ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1453ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final double radians = Math.toRadians(degrees); 1454ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final float xCenter = mXCenter + centerRadius * (float) Math.sin(radians); 1455ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final float yCenter = mYCenter - centerRadius * (float) Math.cos(radians); 1456ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1457ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette bounds.set((int) (xCenter - radius), (int) (yCenter - radius), 1458ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette (int) (xCenter + radius), (int) (yCenter + radius)); 1459ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1460ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1461ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private CharSequence getVirtualViewDescription(int type, int value) { 1462ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final CharSequence description; 1463ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (type == TYPE_HOUR || type == TYPE_MINUTE) { 1464ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette description = Integer.toString(value); 1465ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else { 1466ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette description = null; 1467ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1468ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return description; 1469ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1470ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1471ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private boolean isVirtualViewSelected(int type, int value) { 1472ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette final boolean selected; 1473ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette if (type == TYPE_HOUR) { 1474ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette selected = getCurrentHour() == value; 1475ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else if (type == TYPE_MINUTE) { 1476ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette selected = getCurrentMinute() == value; 1477ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } else { 1478ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette selected = false; 1479ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1480ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return selected; 1481ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1482ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1483ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private int makeId(int type, int value) { 1484ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return type << SHIFT_TYPE | value << SHIFT_VALUE; 1485ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1486ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1487ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private int getTypeFromId(int id) { 1488ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return id >>> SHIFT_TYPE & MASK_TYPE; 1489ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1490ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette 1491ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette private int getValueFromId(int id) { 1492ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette return id >>> SHIFT_VALUE & MASK_VALUE; 1493ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette } 1494eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio } 1495eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette 1496eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette private static class IntHolder { 1497eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette private int mValue; 1498eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette 1499eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette public IntHolder(int value) { 1500eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mValue = value; 1501eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette } 1502eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette 1503eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette public void setValue(int value) { 1504eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette mValue = value; 1505eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette } 1506eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette 1507eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette public int getValue() { 1508eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette return mValue; 1509eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette } 1510eb1d3798e37572ca515aad572350f5745adf023dAlan Viverette } 1511eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio} 1512