/**
   * 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;
  }
 /**
  * Calculate the degrees within the circle that corresponds to the specified coordinates, if the
  * coordinates are within the range that will trigger a selection.
  *
  * @param pointX The x coordinate.
  * @param pointY The y coordinate.
  * @param forceLegal Force the selection to be legal, regardless of how far the coordinates are
  *     from the actual numbers.
  * @param isInnerCircle If the selection may be in the inner circle, pass in a size-1 boolean
  *     array here, inside which the value will be true if the selection is in the inner circle,
  *     and false if in the outer circle.
  * @return Degrees from 0 to 360, if the selection was within the legal range. -1 if not.
  */
 private int getDegreesFromCoords(
     float pointX, float pointY, boolean forceLegal, final Boolean[] isInnerCircle) {
   int currentItem = getCurrentItemShowing();
   if (currentItem == HOUR_INDEX) {
     return mHourRadialSelectorView.getDegreesFromCoords(
         pointX, pointY, forceLegal, isInnerCircle);
   } else if (currentItem == MINUTE_INDEX) {
     return mMinuteRadialSelectorView.getDegreesFromCoords(
         pointX, pointY, forceLegal, isInnerCircle);
   } else {
     return -1;
   }
 }
 /** Set either the hour or the minute. Will set the internal value, and set the selection. */
 private void setItem(int index, int value) {
   if (index == HOUR_INDEX) {
     setValueForItem(HOUR_INDEX, value);
     int hourDegrees = (value % 12) * HOUR_VALUE_TO_DEGREES_STEP_SIZE;
     mHourRadialSelectorView.setSelection(hourDegrees, isHourInnerCircle(value), false);
     mHourRadialSelectorView.invalidate();
   } else if (index == MINUTE_INDEX) {
     setValueForItem(MINUTE_INDEX, value);
     int minuteDegrees = value * MINUTE_VALUE_TO_DEGREES_STEP_SIZE;
     mMinuteRadialSelectorView.setSelection(minuteDegrees, false, false);
     mMinuteRadialSelectorView.invalidate();
   }
 }
  /**
   * For the currently showing view (either hours or minutes), re-calculate the position for the
   * selector, and redraw it at that position. The input degrees will be snapped to a selectable
   * value.
   *
   * @param degrees Degrees which should be selected.
   * @param isInnerCircle Whether the selection should be in the inner circle; will be ignored if
   *     there is no inner circle.
   * @param forceToVisibleValue Even if the currently-showing circle allows for fine-grained
   *     selection (i.e. minutes), force the selection to one of the visibly-showing values.
   * @param forceDrawDot The dot in the circle will generally only be shown when the selection is on
   *     non-visible values, but use this to force the dot to be shown.
   * @return The value that was selected, i.e. 0-23 for hours, 0-59 for minutes.
   */
  private int reselectSelector(
      int degrees, boolean isInnerCircle, boolean forceToVisibleValue, boolean forceDrawDot) {
    if (degrees == -1) {
      return -1;
    }
    int currentShowing = getCurrentItemShowing();

    int stepSize;
    boolean allowFineGrained = !forceToVisibleValue && (currentShowing == MINUTE_INDEX);
    if (allowFineGrained) {
      degrees = snapPrefer30s(degrees);
    } else {
      degrees = snapOnly30s(degrees, 0);
    }

    RadialSelectorView radialSelectorView;
    if (currentShowing == HOUR_INDEX) {
      radialSelectorView = mHourRadialSelectorView;
      stepSize = HOUR_VALUE_TO_DEGREES_STEP_SIZE;
    } else {
      radialSelectorView = mMinuteRadialSelectorView;
      stepSize = MINUTE_VALUE_TO_DEGREES_STEP_SIZE;
    }
    radialSelectorView.setSelection(degrees, isInnerCircle, forceDrawDot);
    radialSelectorView.invalidate();

    if (currentShowing == HOUR_INDEX) {
      if (mIs24HourMode) {
        if (degrees == 0 && isInnerCircle) {
          degrees = 360;
        } else if (degrees == 360 && !isInnerCircle) {
          degrees = 0;
        }
      } else if (degrees == 0) {
        degrees = 360;
      }
    } else if (degrees == 360 && currentShowing == MINUTE_INDEX) {
      degrees = 0;
    }

    int value = degrees / stepSize;
    if (currentShowing == HOUR_INDEX && mIs24HourMode && !isInnerCircle && degrees != 0) {
      value += 12;
    }
    return value;
  }
  /**
   * Set either minutes or hours as showing.
   *
   * @param animate True to animate the transition, false to show with no animation.
   */
  @SuppressLint("NewApi")
  public void setCurrentItemShowing(int index, boolean animate) {
    if (index != HOUR_INDEX && index != MINUTE_INDEX) {
      Log.e(TAG, "TimePicker does not support view at index " + index);
      return;
    }

    // NineOldDroids does not work in this case due to denepency recursion.
    animate = animate && Build.VERSION.SDK_INT >= 14;

    int lastIndex = getCurrentItemShowing();
    mCurrentItemShowing = index;

    if (animate && (index != lastIndex)) {
      ObjectAnimator[] anims = new ObjectAnimator[4];
      if (index == MINUTE_INDEX) {
        anims[0] = mHourRadialTextsView.getDisappearAnimator();
        anims[1] = mHourRadialSelectorView.getDisappearAnimator();
        anims[2] = mMinuteRadialTextsView.getReappearAnimator();
        anims[3] = mMinuteRadialSelectorView.getReappearAnimator();
      } else if (index == HOUR_INDEX) {
        anims[0] = mHourRadialTextsView.getReappearAnimator();
        anims[1] = mHourRadialSelectorView.getReappearAnimator();
        anims[2] = mMinuteRadialTextsView.getDisappearAnimator();
        anims[3] = mMinuteRadialSelectorView.getDisappearAnimator();
      }

      if (mTransition != null && mTransition.isRunning()) {
        mTransition.end();
      }
      mTransition = new AnimatorSet();
      mTransition.playTogether(anims);
      mTransition.start();
    } else {
      if (Build.VERSION.SDK_INT >= 11) {
        int hourAlpha = (index == HOUR_INDEX) ? 255 : 0;
        int minuteAlpha = (index == MINUTE_INDEX) ? 255 : 0;
        mHourRadialTextsView.setAlpha(hourAlpha);
        mHourRadialSelectorView.setAlpha(hourAlpha);
        mMinuteRadialTextsView.setAlpha(minuteAlpha);
        mMinuteRadialSelectorView.setAlpha(minuteAlpha);
      } else {
        int hourVisibility = (index == HOUR_INDEX) ? View.VISIBLE : View.INVISIBLE;
        int minuteVisibility = (index == MINUTE_INDEX) ? View.VISIBLE : View.INVISIBLE;
        mHourRadialTextsView.setVisibility(hourVisibility);
        mHourRadialSelectorView.setVisibility(hourVisibility);
        mMinuteRadialTextsView.setVisibility(minuteVisibility);
        mMinuteRadialSelectorView.setVisibility(minuteVisibility);
      }
    }
  }