content: /*
   * Copyright (C) 2008 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.os;
  
  /**
   * Schedule a countdown until a time in the future, with
   * regular notifications on intervals along the way.
   *
   * Example of showing a 30 second countdown in a text field:
   *
   * <pre class="prettyprint">
   * new CountDownTimer(30000, 1000) {
   *
   *     public void onTick(long millisUntilFinished) {
   *         mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
   *     }
   *
   *     public void onFinish() {
   *         mTextField.setText("done!");
   *     }
   *  }.start();
   * </pre>
   *
   * The calls to {@link #onTick(long)} are synchronized to this object so that
   * one call to {@link #onTick(long)} won't ever occur before the previous
   * callback is complete.  This is only relevant when the implementation of
   * {@link #onTick(long)} takes an amount of time to execute that is significant
   * compared to the countdown interval.
   */
  public abstract class CountDownTimer {
  
      /**
       * Millis since epoch when alarm should stop.
       */
      private final long mMillisInFuture;
  
      /**
       * The interval in millis that the user receives callbacks
       */
      private final long mCountdownInterval;
  
      private long mStopTimeInFuture;
      
      /**
      * boolean representing if the timer was cancelled
      */
      private boolean mCancelled = false;
  
      /**
       * @param millisInFuture The number of millis in the future from the call
       *   to {@link #start()} until the countdown is done and {@link #onFinish()}
       *   is called.
       * @param countDownInterval The interval along the way to receive
       *   {@link #onTick(long)} callbacks.
       */
      public CountDownTimer(long millisInFuture, long countDownInterval) {
          mMillisInFuture = millisInFuture;
          mCountdownInterval = countDownInterval;
      }
  
      /**
       * Cancel the countdown.
       */
      public synchronized final void cancel() {
          mCancelled = true;
          mHandler.removeMessages(MSG);
      }
  
      /**
       * Start the countdown.
       */
      public synchronized final CountDownTimer start() {
          mCancelled = false;
          if (mMillisInFuture <= 0) {
              onFinish();
              return this;
          }
          mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
          mHandler.sendMessage(mHandler.obtainMessage(MSG));
          return this;
      }
  
  
      /**
       * Callback fired on regular interval.
       * @param millisUntilFinished The amount of time until finished.
       */
      public abstract void onTick(long millisUntilFinished);
  
      /**
       * Callback fired when the time is up.
       */
      public abstract void onFinish();
  
  
      private static final int MSG = 1;
  
  
      // handles counting down
      private Handler mHandler = new Handler() {
  
          @Override
          public void handleMessage(Message msg) {
  
              synchronized (CountDownTimer.this) {
                  if (mCancelled) {
                      return;
                  }
  
                  final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
  
                  if (millisLeft <= 0) {
                      onFinish();
                  } else {
                      long lastTickStart = SystemClock.elapsedRealtime();
                      onTick(millisLeft);
  
                      // take into account user's onTick taking time to execute
                      long lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart;
                      long delay;
  
                      if (millisLeft < mCountdownInterval) {
                          // just delay until done
                          delay = millisLeft - lastTickDuration;
  
                          // special case: user's onTick took more than interval to
                          // complete, trigger onFinish without delay
                          if (delay < 0) delay = 0;
                      } else {
                          delay = mCountdownInterval - lastTickDuration;
  
                          // special case: user's onTick took more than interval to
                          // complete, skip to next interval
                          while (delay < 0) delay += mCountdownInterval;
                      }
  
                      sendMessageDelayed(obtainMessage(MSG), delay);
                  }
              }
          }
      };
  }
  