/** * Initialize the Layout with starting values. * * @param context * @param initialHoursOfDay * @param initialMinutes * @param is24HourMode */ public void initialize( Context context, int initialHoursOfDay, int initialMinutes, boolean is24HourMode, boolean vibrate) { if (mTimeInitialized) { Log.e(TAG, "Time has already been initialized."); return; } mIs24HourMode = is24HourMode; mHideAmPm = Utils.isTouchExplorationEnabled(mAccessibilityManager) ? true : mIs24HourMode; mVibrate = vibrate; // Initialize the circle and AM/PM circles if applicable. mCircleView.initialize(context, mHideAmPm); mCircleView.invalidate(); if (!mHideAmPm) { mAmPmCirclesView.initialize(context, initialHoursOfDay < 12 ? AM : PM); mAmPmCirclesView.invalidate(); } // Initialize the hours and minutes numbers. Resources res = context.getResources(); int[] hours = {12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; int[] hours_24 = {0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; int[] minutes = {0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55}; String[] hoursTexts = new String[12]; String[] innerHoursTexts = new String[12]; String[] minutesTexts = new String[12]; for (int i = 0; i < 12; i++) { hoursTexts[i] = is24HourMode ? String.format("%02d", hours_24[i]) : String.format("%d", hours[i]); innerHoursTexts[i] = String.format("%d", hours[i]); minutesTexts[i] = String.format("%02d", minutes[i]); } mHourRadialTextsView.initialize( res, hoursTexts, (is24HourMode ? innerHoursTexts : null), mHideAmPm, true); mHourRadialTextsView.invalidate(); mMinuteRadialTextsView.initialize(res, minutesTexts, null, mHideAmPm, false); mMinuteRadialTextsView.invalidate(); // Initialize the currently-selected hour and minute. setValueForItem(HOUR_INDEX, initialHoursOfDay); setValueForItem(MINUTE_INDEX, initialMinutes); int hourDegrees = (initialHoursOfDay % 12) * HOUR_VALUE_TO_DEGREES_STEP_SIZE; mHourRadialSelectorView.initialize( context, mHideAmPm, is24HourMode, true, hourDegrees, isHourInnerCircle(initialHoursOfDay)); int minuteDegrees = initialMinutes * MINUTE_VALUE_TO_DEGREES_STEP_SIZE; mMinuteRadialSelectorView.initialize(context, mHideAmPm, false, false, minuteDegrees, false); mTimeInitialized = true; }
/** * Initialize this selector with the state of the picker. * * @param context Current context. * @param is24HourMode Whether the selector is in 24-hour mode, which will tell us whether the * circle's center is moved up slightly to make room for the AM/PM circles. * @param hasInnerCircle Whether we have both an inner and an outer circle of numbers that may be * selected. Should be true for 24-hour mode in the hours circle. * @param disappearsOut Whether the numbers' animation will have them disappearing out or * disappearing in. * @param selectionDegrees The initial degrees to be selected. * @param isInnerCircle Whether the initial selection is in the inner or outer circle. Will be * ignored when hasInnerCircle is false. */ public void initialize( Context context, boolean is24HourMode, boolean hasInnerCircle, boolean disappearsOut, int selectionDegrees, boolean isInnerCircle) { if (mIsInitialized) { Log.e(TAG, "This RadialSelectorView may only be initialized once."); return; } Resources res = context.getResources(); int blue = Utils.resolveColor(getContext(), R.attr.theme_color, res.getColor(R.color.comm_blue)); mPaint.setColor(blue); mPaint.setAntiAlias(true); // Calculate values for the circle radius size. mIs24HourMode = is24HourMode; if (is24HourMode) { mCircleRadiusMultiplier = Float.parseFloat(res.getString(R.string.circle_radius_multiplier_24HourMode)); } else { mCircleRadiusMultiplier = Float.parseFloat(res.getString(R.string.circle_radius_multiplier)); mAmPmCircleRadiusMultiplier = Float.parseFloat(res.getString(R.string.ampm_circle_radius_multiplier)); } // Calculate values for the radius size(s) of the numbers circle(s). mHasInnerCircle = hasInnerCircle; if (hasInnerCircle) { mInnerNumbersRadiusMultiplier = Float.parseFloat(res.getString(R.string.numbers_radius_multiplier_inner)); mOuterNumbersRadiusMultiplier = Float.parseFloat(res.getString(R.string.numbers_radius_multiplier_outer)); } else { mNumbersRadiusMultiplier = Float.parseFloat(res.getString(R.string.numbers_radius_multiplier_normal)); } mSelectionRadiusMultiplier = Float.parseFloat(res.getString(R.string.selection_radius_multiplier)); // Calculate values for the transition mid-way states. mAnimationRadiusMultiplier = 1; mTransitionMidRadiusMultiplier = 1f + (0.05f * (disappearsOut ? -1 : 1)); mTransitionEndRadiusMultiplier = 1f + (0.3f * (disappearsOut ? 1 : -1)); mInvalidateUpdateListener = new InvalidateUpdateListener(); setSelection(selectionDegrees, isInnerCircle, false); mIsInitialized = true; }
@Override public boolean onTouch(View v, MotionEvent event) { final float eventX = event.getX(); final float eventY = event.getY(); int degrees; int value; final Boolean[] isInnerCircle = new Boolean[1]; isInnerCircle[0] = false; long millis = SystemClock.uptimeMillis(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (!mInputEnabled) { return true; } mDownX = eventX; mDownY = eventY; mLastValueSelected = -1; mDoingMove = false; mDoingTouch = true; // If we're showing the AM/PM, check to see if the user is touching it. if (!mHideAmPm) { mIsTouchingAmOrPm = mAmPmCirclesView.getIsTouchingAmOrPm(eventX, eventY); } else { mIsTouchingAmOrPm = -1; } if (mIsTouchingAmOrPm == AM || mIsTouchingAmOrPm == PM) { // If the touch is on AM or PM, set it as "touched" after the TAP_TIMEOUT // in case the user moves their finger quickly. tryVibrate(); mDownDegrees = -1; mHandler.postDelayed( new Runnable() { @Override public void run() { mAmPmCirclesView.setAmOrPmPressed(mIsTouchingAmOrPm); mAmPmCirclesView.invalidate(); } }, TAP_TIMEOUT); } else { // If we're in accessibility mode, force the touch to be legal. Otherwise, // it will only register within the given touch target zone. boolean forceLegal = Utils.isTouchExplorationEnabled(mAccessibilityManager); // Calculate the degrees that is currently being touched. mDownDegrees = getDegreesFromCoords(eventX, eventY, forceLegal, isInnerCircle); if (mDownDegrees != -1) { // If it's a legal touch, set that number as "selected" after the // TAP_TIMEOUT in case the user moves their finger quickly. tryVibrate(); mHandler.postDelayed( new Runnable() { @Override public void run() { mDoingMove = true; int value = reselectSelector(mDownDegrees, isInnerCircle[0], false, true); mLastValueSelected = value; mListener.onValueSelected(getCurrentItemShowing(), value, false); } }, TAP_TIMEOUT); } } return true; case MotionEvent.ACTION_MOVE: if (!mInputEnabled) { // We shouldn't be in this state, because input is disabled. Log.e(TAG, "Input was disabled, but received ACTION_MOVE."); return true; } float dY = Math.abs(eventY - mDownY); float dX = Math.abs(eventX - mDownX); if (!mDoingMove && dX <= TOUCH_SLOP && dY <= TOUCH_SLOP) { // Hasn't registered down yet, just slight, accidental movement of finger. break; } // If we're in the middle of touching down on AM or PM, check if we still are. // If so, no-op. If not, remove its pressed state. Either way, no need to check // for touches on the other circle. if (mIsTouchingAmOrPm == AM || mIsTouchingAmOrPm == PM) { mHandler.removeCallbacksAndMessages(null); int isTouchingAmOrPm = mAmPmCirclesView.getIsTouchingAmOrPm(eventX, eventY); if (isTouchingAmOrPm != mIsTouchingAmOrPm) { mAmPmCirclesView.setAmOrPmPressed(-1); mAmPmCirclesView.invalidate(); mIsTouchingAmOrPm = -1; } break; } if (mDownDegrees == -1) { // Original down was illegal, so no movement will register. break; } // We're doing a move along the circle, so move the selection as appropriate. mDoingMove = true; mHandler.removeCallbacksAndMessages(null); degrees = getDegreesFromCoords(eventX, eventY, true, isInnerCircle); if (degrees != -1) { value = reselectSelector(degrees, isInnerCircle[0], false, true); if (value != mLastValueSelected) { tryVibrate(); mLastValueSelected = value; mListener.onValueSelected(getCurrentItemShowing(), value, false); } } return true; case MotionEvent.ACTION_UP: if (!mInputEnabled) { // If our touch input was disabled, tell the listener to re-enable us. Log.d(TAG, "Input was disabled, but received ACTION_UP."); mListener.onValueSelected(ENABLE_PICKER_INDEX, 1, false); return true; } mHandler.removeCallbacksAndMessages(null); mDoingTouch = false; // If we're touching AM or PM, set it as selected, and tell the listener. if (mIsTouchingAmOrPm == AM || mIsTouchingAmOrPm == PM) { int isTouchingAmOrPm = mAmPmCirclesView.getIsTouchingAmOrPm(eventX, eventY); mAmPmCirclesView.setAmOrPmPressed(-1); mAmPmCirclesView.invalidate(); if (isTouchingAmOrPm == mIsTouchingAmOrPm) { mAmPmCirclesView.setAmOrPm(isTouchingAmOrPm); if (getIsCurrentlyAmOrPm() != isTouchingAmOrPm) { mListener.onValueSelected(AMPM_INDEX, mIsTouchingAmOrPm, false); setValueForItem(AMPM_INDEX, isTouchingAmOrPm); } } mIsTouchingAmOrPm = -1; break; } // If we have a legal degrees selected, set the value and tell the listener. if (mDownDegrees != -1) { degrees = getDegreesFromCoords(eventX, eventY, mDoingMove, isInnerCircle); if (degrees != -1) { value = reselectSelector(degrees, isInnerCircle[0], !mDoingMove, false); if (getCurrentItemShowing() == HOUR_INDEX && !mIs24HourMode) { int amOrPm = getIsCurrentlyAmOrPm(); if (amOrPm == AM && value == 12) { value = 0; } else if (amOrPm == PM && value != 12) { value += 12; } } setValueForItem(getCurrentItemShowing(), value); mListener.onValueSelected(getCurrentItemShowing(), value, true); } } mDoingMove = false; return true; default: break; } return false; }