194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng/*
294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * Copyright (C) 2011 The Android Open Source Project
394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng *
494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * Licensed under the Apache License, Version 2.0 (the "License");
594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * you may not use this file except in compliance with the License.
694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * You may obtain a copy of the License at
794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng *
894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng *      http://www.apache.org/licenses/LICENSE-2.0
994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng *
1094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * Unless required by applicable law or agreed to in writing, software
1194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * distributed under the License is distributed on an "AS IS" BASIS,
1294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * See the License for the specific language governing permissions and
1494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * limitations under the License.
1594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng */
1694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
1794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengpackage com.android.dialer.voicemail;
1894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
1994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport android.content.Context;
2094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport android.database.ContentObserver;
2194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport android.media.AudioManager;
2294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport android.media.MediaPlayer;
2394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport android.net.Uri;
2494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport android.os.AsyncTask;
2594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport android.os.Bundle;
2694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport android.os.Handler;
2794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport android.os.PowerManager;
2894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport android.view.View;
2994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport android.widget.SeekBar;
3094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
319554500572ba82fbd7adb0a1637206ef870ef09eChiao Chengimport com.android.dialer.R;
3291197049c458f07092b31501d2ed512180b13d58Chiao Chengimport com.android.dialer.util.AsyncTaskExecutor;
3394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport com.android.ex.variablespeed.MediaPlayerProxy;
3494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport com.android.ex.variablespeed.SingleThreadedMediaPlayerProxy;
3594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport com.google.common.annotations.VisibleForTesting;
3694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport com.google.common.base.Preconditions;
3794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
3856e34715b973f0eed3b4a0cec278ea412a89844dJay Shraunerimport java.util.concurrent.RejectedExecutionException;
3994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport java.util.concurrent.ScheduledExecutorService;
4094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport java.util.concurrent.ScheduledFuture;
4194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport java.util.concurrent.TimeUnit;
4294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport java.util.concurrent.atomic.AtomicBoolean;
4394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport java.util.concurrent.atomic.AtomicInteger;
4494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
4594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport javax.annotation.concurrent.GuardedBy;
4694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport javax.annotation.concurrent.NotThreadSafe;
4794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengimport javax.annotation.concurrent.ThreadSafe;
4894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
4994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng/**
5094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * Contains the controlling logic for a voicemail playback ui.
5194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * <p>
5294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * Specifically right now this class is used to control the
5394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * {@link com.android.dialer.voicemail.VoicemailPlaybackFragment}.
5494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * <p>
5594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * This class is not thread safe. The thread policy for this class is
5694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * thread-confinement, all calls into this class from outside must be done from
5794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng * the main ui thread.
5894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng */
5994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng@NotThreadSafe
6094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng@VisibleForTesting
6194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Chengpublic class VoicemailPlaybackPresenter {
6294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /** The stream used to playback voicemail. */
6394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private static final int PLAYBACK_STREAM = AudioManager.STREAM_VOICE_CALL;
6494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
6594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /** Contract describing the behaviour we need from the ui we are controlling. */
6694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    public interface PlaybackView {
6794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        Context getDataSourceContext();
6894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void runOnUiThread(Runnable runnable);
6994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void setStartStopListener(View.OnClickListener listener);
7094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void setPositionSeekListener(SeekBar.OnSeekBarChangeListener listener);
7194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void setSpeakerphoneListener(View.OnClickListener listener);
7294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void setIsBuffering();
7394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void setClipPosition(int clipPositionInMillis, int clipLengthInMillis);
7494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        int getDesiredClipPosition();
7594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void playbackStarted();
7694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void playbackStopped();
7794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void playbackError(Exception e);
7894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        boolean isSpeakerPhoneOn();
7994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void setSpeakerPhoneOn(boolean on);
8094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void finish();
8194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void setRateDisplay(float rate, int stringResourceId);
8294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void setRateIncreaseButtonListener(View.OnClickListener listener);
8394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void setRateDecreaseButtonListener(View.OnClickListener listener);
8494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void setIsFetchingContent();
8594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void disableUiElements();
8694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void enableUiElements();
8794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void sendFetchVoicemailRequest(Uri voicemailUri);
8894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        boolean queryHasContent(Uri voicemailUri);
8994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void setFetchContentTimeout();
9094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void registerContentObserver(Uri uri, ContentObserver observer);
9194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void unregisterContentObserver(ContentObserver observer);
9294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void enableProximitySensor();
9394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void disableProximitySensor();
9494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        void setVolumeControlStream(int streamType);
9594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
9694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
9794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /** The enumeration of {@link AsyncTask} objects we use in this class. */
9894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    public enum Tasks {
9994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        CHECK_FOR_CONTENT,
10094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        CHECK_CONTENT_AFTER_CHANGE,
10194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        PREPARE_MEDIA_PLAYER,
10294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        RESET_PREPARE_START_MEDIA_PLAYER,
10394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
10494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
10594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /** Update rate for the slider, 30fps. */
10694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private static final int SLIDER_UPDATE_PERIOD_MILLIS = 1000 / 30;
10794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /** Time our ui will wait for content to be fetched before reporting not available. */
10894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private static final long FETCH_CONTENT_TIMEOUT_MS = 20000;
10994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /**
11094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * If present in the saved instance bundle, we should not resume playback on
11194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * create.
11294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     */
11394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private static final String PAUSED_STATE_KEY = VoicemailPlaybackPresenter.class.getName()
11494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            + ".PAUSED_STATE_KEY";
11594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /**
11694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * If present in the saved instance bundle, indicates where to set the
11794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * playback slider.
11894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     */
11994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private static final String CLIP_POSITION_KEY = VoicemailPlaybackPresenter.class.getName()
12094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            + ".CLIP_POSITION_KEY";
12194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
12294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /** The preset variable-speed rates.  Each is greater than the previous by 25%. */
12394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private static final float[] PRESET_RATES = new float[] {
12494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        0.64f, 0.8f, 1.0f, 1.25f, 1.5625f
12594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    };
12694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /** The string resource ids corresponding to the names given to the above preset rates. */
12794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private static final int[] PRESET_NAMES = new int[] {
12894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        R.string.voicemail_speed_slowest,
12994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        R.string.voicemail_speed_slower,
13094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        R.string.voicemail_speed_normal,
13194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        R.string.voicemail_speed_faster,
13294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        R.string.voicemail_speed_fastest,
13394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    };
13494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
13594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /**
13694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * Pointer into the {@link VoicemailPlaybackPresenter#PRESET_RATES} array.
13794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * <p>
13894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * This doesn't need to be synchronized, it's used only by the {@link RateChangeListener}
13994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * which in turn is only executed on the ui thread.  This can't be encapsulated inside the
14094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * rate change listener since multiple rate change listeners must share the same value.
14194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     */
14294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private int mRateIndex = 2;
14394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
14494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /**
14594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * The most recently calculated duration.
14694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * <p>
14794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * We cache this in a field since we don't want to keep requesting it from the player, as
14894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * this can easily lead to throwing {@link IllegalStateException} (any time the player is
14994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * released, it's illegal to ask for the duration).
15094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     */
15194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private final AtomicInteger mDuration = new AtomicInteger(0);
15294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
15394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private final PlaybackView mView;
15494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private final MediaPlayerProxy mPlayer;
15594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private final PositionUpdater mPositionUpdater;
15694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
15794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /** Voicemail uri to play. */
15894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private final Uri mVoicemailUri;
15994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /** Start playing in onCreate iff this is true. */
16094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private final boolean mStartPlayingImmediately;
16194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /** Used to run async tasks that need to interact with the ui. */
16294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private final AsyncTaskExecutor mAsyncTaskExecutor;
16394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
16494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /**
16594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * Used to handle the result of a successful or time-out fetch result.
16694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * <p>
16794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * This variable is thread-contained, accessed only on the ui thread.
16894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     */
16994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private FetchResultHandler mFetchResultHandler;
17094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private PowerManager.WakeLock mWakeLock;
17194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private AsyncTask<Void, ?, ?> mPrepareTask;
17294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
17394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    public VoicemailPlaybackPresenter(PlaybackView view, MediaPlayerProxy player,
17494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            Uri voicemailUri, ScheduledExecutorService executorService,
17594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            boolean startPlayingImmediately, AsyncTaskExecutor asyncTaskExecutor,
17694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            PowerManager.WakeLock wakeLock) {
17794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView = view;
17894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mPlayer = player;
17994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mVoicemailUri = voicemailUri;
18094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mStartPlayingImmediately = startPlayingImmediately;
18194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mAsyncTaskExecutor = asyncTaskExecutor;
18294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mPositionUpdater = new PositionUpdater(executorService, SLIDER_UPDATE_PERIOD_MILLIS);
18394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mWakeLock = wakeLock;
18494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
18594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
18694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    public void onCreate(Bundle bundle) {
18794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.setVolumeControlStream(PLAYBACK_STREAM);
18894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        checkThatWeHaveContent();
18994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
19094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
19194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /**
19294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * Checks to see if we have content available for this voicemail.
19394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * <p>
19494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * This method will be called once, after the fragment has been created, before we know if the
19594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * voicemail we've been asked to play has any content available.
19694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * <p>
19794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * This method will notify the user through the ui that we are fetching the content, then check
19894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * to see if the content field in the db is set. If set, we proceed to
19994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * {@link #postSuccessfullyFetchedContent()} method. If not set, we will make a request to fetch
20094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * the content asynchronously via {@link #makeRequestForContent()}.
20194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     */
20294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private void checkThatWeHaveContent() {
20394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.setIsFetchingContent();
20494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mAsyncTaskExecutor.submit(Tasks.CHECK_FOR_CONTENT, new AsyncTask<Void, Void, Boolean>() {
20594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            @Override
20694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            public Boolean doInBackground(Void... params) {
20794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                return mView.queryHasContent(mVoicemailUri);
20894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            }
20994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
21094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            @Override
21194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            public void onPostExecute(Boolean hasContent) {
21294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                if (hasContent) {
21394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    postSuccessfullyFetchedContent();
21494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                } else {
21594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    makeRequestForContent();
21694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                }
21794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            }
21894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        });
21994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
22094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
22194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /**
22294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * Makes a broadcast request to ask that a voicemail source fetch this content.
22394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * <p>
22494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * This method <b>must be called on the ui thread</b>.
22594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * <p>
22694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * This method will be called when we realise that we don't have content for this voicemail. It
22794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * will trigger a broadcast to request that the content be downloaded. It will add a listener to
22894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * the content resolver so that it will be notified when the has_content field changes. It will
22994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * also set a timer. If the has_content field changes to true within the allowed time, we will
23094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * proceed to {@link #postSuccessfullyFetchedContent()}. If the has_content field does not
23194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * become true within the allowed time, we will update the ui to reflect the fact that content
23294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * was not available.
23394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     */
23494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private void makeRequestForContent() {
23594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        Handler handler = new Handler();
23694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        Preconditions.checkState(mFetchResultHandler == null, "mFetchResultHandler should be null");
23794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mFetchResultHandler = new FetchResultHandler(handler);
23894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.registerContentObserver(mVoicemailUri, mFetchResultHandler);
23994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        handler.postDelayed(mFetchResultHandler.getTimeoutRunnable(), FETCH_CONTENT_TIMEOUT_MS);
24094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.sendFetchVoicemailRequest(mVoicemailUri);
24194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
24294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
24394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    @ThreadSafe
24494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private class FetchResultHandler extends ContentObserver implements Runnable {
24594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        private AtomicBoolean mResultStillPending = new AtomicBoolean(true);
24694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        private final Handler mHandler;
24794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
24894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public FetchResultHandler(Handler handler) {
24994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            super(handler);
25094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mHandler = handler;
25194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
25294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
25394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public Runnable getTimeoutRunnable() {
25494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            return this;
25594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
25694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
25794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        @Override
25894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public void run() {
25994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            if (mResultStillPending.getAndSet(false)) {
26094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                mView.unregisterContentObserver(FetchResultHandler.this);
26194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                mView.setFetchContentTimeout();
26294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            }
26394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
26494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
26594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public void destroy() {
26694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            if (mResultStillPending.getAndSet(false)) {
26794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                mView.unregisterContentObserver(FetchResultHandler.this);
26894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                mHandler.removeCallbacks(this);
26994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            }
27094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
27194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
27294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        @Override
27394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public void onChange(boolean selfChange) {
27494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mAsyncTaskExecutor.submit(Tasks.CHECK_CONTENT_AFTER_CHANGE,
27594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    new AsyncTask<Void, Void, Boolean>() {
27694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                @Override
27794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                public Boolean doInBackground(Void... params) {
27894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    return mView.queryHasContent(mVoicemailUri);
27994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                }
28094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
28194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                @Override
28294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                public void onPostExecute(Boolean hasContent) {
28394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    if (hasContent) {
28494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                        if (mResultStillPending.getAndSet(false)) {
28594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                            mView.unregisterContentObserver(FetchResultHandler.this);
28694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                            postSuccessfullyFetchedContent();
28794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                        }
28894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    }
28994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                }
29094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            });
29194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
29294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
29394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
29494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /**
29594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * Prepares the voicemail content for playback.
29694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * <p>
29794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * This method will be called once we know that our voicemail has content (according to the
29894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * content provider). This method will try to prepare the data source through the media player.
29994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * If preparing the media player works, we will call through to
30094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * {@link #postSuccessfulPrepareActions()}. If preparing the media player fails (perhaps the
30194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * file the content provider points to is actually missing, perhaps it is of an unknown file
30294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * format that we can't play, who knows) then we will show an error on the ui.
30394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     */
30494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private void postSuccessfullyFetchedContent() {
30594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.setIsBuffering();
30694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mAsyncTaskExecutor.submit(Tasks.PREPARE_MEDIA_PLAYER,
30794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                new AsyncTask<Void, Void, Exception>() {
30894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    @Override
30994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    public Exception doInBackground(Void... params) {
31094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                        try {
31194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                            mPlayer.reset();
31294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                            mPlayer.setDataSource(mView.getDataSourceContext(), mVoicemailUri);
31394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                            mPlayer.setAudioStreamType(PLAYBACK_STREAM);
31494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                            mPlayer.prepare();
31556e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                            mDuration.set(mPlayer.getDuration());
31694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                            return null;
31794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                        } catch (Exception e) {
31894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                            return e;
31994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                        }
32094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    }
32194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
32294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    @Override
32394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    public void onPostExecute(Exception exception) {
32494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                        if (exception == null) {
32594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                            postSuccessfulPrepareActions();
32694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                        } else {
32794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                            mView.playbackError(exception);
32894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                        }
32994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    }
33094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                });
33194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
33294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
33394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /**
33494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * Enables the ui, and optionally starts playback immediately.
33594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * <p>
33694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * This will be called once we have successfully prepared the media player, and will optionally
33794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * playback immediately.
33894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     */
33994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private void postSuccessfulPrepareActions() {
34094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.enableUiElements();
34194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.setPositionSeekListener(new PlaybackPositionListener());
34294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.setStartStopListener(new StartStopButtonListener());
34394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.setSpeakerphoneListener(new SpeakerphoneListener());
34494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mPlayer.setOnErrorListener(new MediaPlayerErrorListener());
34594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mPlayer.setOnCompletionListener(new MediaPlayerCompletionListener());
34694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.setSpeakerPhoneOn(mView.isSpeakerPhoneOn());
34794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.setRateDecreaseButtonListener(createRateDecreaseListener());
34894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.setRateIncreaseButtonListener(createRateIncreaseListener());
34956e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner        mView.setClipPosition(0, mDuration.get());
35094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.playbackStopped();
35194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        // Always disable on stop.
35294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.disableProximitySensor();
35394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        if (mStartPlayingImmediately) {
35494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            resetPrepareStartPlaying(0);
35594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
35694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        // TODO: Now I'm ignoring the bundle, when previously I was checking for contains against
35794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        // the PAUSED_STATE_KEY, and CLIP_POSITION_KEY.
35894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
35994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
36094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    public void onSaveInstanceState(Bundle outState) {
36194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        outState.putInt(CLIP_POSITION_KEY, mView.getDesiredClipPosition());
36294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        if (!mPlayer.isPlaying()) {
36394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            outState.putBoolean(PAUSED_STATE_KEY, true);
36494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
36594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
36694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
36794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    public void onDestroy() {
36856e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner        if (mPrepareTask != null) {
36956e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner            mPrepareTask.cancel(false);
37056e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner            mPrepareTask = null;
37156e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner        }
37294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mPlayer.release();
37394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        if (mFetchResultHandler != null) {
37494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mFetchResultHandler.destroy();
37594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mFetchResultHandler = null;
37694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
37794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mPositionUpdater.stopUpdating();
37894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        if (mWakeLock.isHeld()) {
37994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mWakeLock.release();
38094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
38194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
38294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
38394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private class MediaPlayerErrorListener implements MediaPlayer.OnErrorListener {
38494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        @Override
38594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public boolean onError(MediaPlayer mp, int what, int extra) {
38694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mView.runOnUiThread(new Runnable() {
38794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                @Override
38894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                public void run() {
38994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    handleError(new IllegalStateException("MediaPlayer error listener invoked"));
39094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                }
39194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            });
39294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            return true;
39394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
39494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
39594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
39694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private class MediaPlayerCompletionListener implements MediaPlayer.OnCompletionListener {
39794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        @Override
39894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public void onCompletion(final MediaPlayer mp) {
39994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mView.runOnUiThread(new Runnable() {
40094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                @Override
40194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                public void run() {
40294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    handleCompletion(mp);
40394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                }
40494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            });
40594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
40694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
40794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
40894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    public View.OnClickListener createRateDecreaseListener() {
40994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        return new RateChangeListener(false);
41094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
41194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
41294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    public View.OnClickListener createRateIncreaseListener() {
41394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        return new RateChangeListener(true);
41494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
41594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
41694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /**
41794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * Listens to clicks on the rate increase and decrease buttons.
41894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * <p>
41994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * This class is not thread-safe, but all interactions with it will happen on the ui thread.
42094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     */
42194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private class RateChangeListener implements View.OnClickListener {
42294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        private final boolean mIncrease;
42394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
42494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public RateChangeListener(boolean increase) {
42594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mIncrease = increase;
42694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
42794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
42894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        @Override
42994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public void onClick(View v) {
43094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            // Adjust the current rate, then clamp it to the allowed values.
43194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mRateIndex = constrain(mRateIndex + (mIncrease ? 1 : -1), 0, PRESET_RATES.length - 1);
43294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            // Whether or not we have actually changed the index, call changeRate().
43394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            // This will ensure that we show the "fastest" or "slowest" text on the ui to indicate
43494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            // to the user that it doesn't get any faster or slower.
43594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            changeRate(PRESET_RATES[mRateIndex], PRESET_NAMES[mRateIndex]);
43694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
43794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
43894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
43956e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner    private class AsyncPrepareTask extends AsyncTask<Void, Void, Exception> {
44056e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner        private int mClipPositionInMillis;
44156e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner
44256e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner        AsyncPrepareTask(int clipPositionInMillis) {
44356e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner            mClipPositionInMillis = clipPositionInMillis;
44456e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner        }
44556e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner
44656e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner        @Override
44756e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner        public Exception doInBackground(Void... params) {
44856e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner            try {
44956e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                if (!mPlayer.isReadyToPlay()) {
45056e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    mPlayer.reset();
45156e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    mPlayer.setDataSource(mView.getDataSourceContext(), mVoicemailUri);
45256e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    mPlayer.setAudioStreamType(PLAYBACK_STREAM);
45356e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    mPlayer.prepare();
45456e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                }
45556e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                return null;
45656e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner            } catch (Exception e) {
45756e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                return e;
45856e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner            }
45956e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner        }
46056e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner
46156e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner        @Override
46256e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner        public void onPostExecute(Exception exception) {
46356e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner            mPrepareTask = null;
46456e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner            if (exception == null) {
46556e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                final int duration = mPlayer.getDuration();
46656e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                mDuration.set(duration);
46756e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                int startPosition =
46856e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    constrain(mClipPositionInMillis, 0, duration);
46956e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                mPlayer.seekTo(startPosition);
47056e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                mView.setClipPosition(startPosition, duration);
47156e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                try {
47256e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    // Can throw RejectedExecutionException
47356e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    mPlayer.start();
47456e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    mView.playbackStarted();
47556e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    if (!mWakeLock.isHeld()) {
47656e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                        mWakeLock.acquire();
47756e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    }
47856e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    // Only enable if we are not currently using the speaker phone.
47956e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    if (!mView.isSpeakerPhoneOn()) {
48056e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                        mView.enableProximitySensor();
48156e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    }
48256e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    // Can throw RejectedExecutionException
48356e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    mPositionUpdater.startUpdating(startPosition, duration);
48456e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                } catch (RejectedExecutionException e) {
48556e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    handleError(e);
48656e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                }
48756e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner            } else {
48856e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                handleError(exception);
48956e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner            }
49056e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner        }
49156e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner    }
49256e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner
49394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private void resetPrepareStartPlaying(final int clipPositionInMillis) {
49494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        if (mPrepareTask != null) {
49594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mPrepareTask.cancel(false);
49656e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner            mPrepareTask = null;
49794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
49894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mPrepareTask = mAsyncTaskExecutor.submit(Tasks.RESET_PREPARE_START_MEDIA_PLAYER,
49956e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                new AsyncPrepareTask(clipPositionInMillis));
50094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
50194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
50294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private void handleError(Exception e) {
50394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.playbackError(e);
50494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mPositionUpdater.stopUpdating();
50594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mPlayer.release();
50694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
50794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
50894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    public void handleCompletion(MediaPlayer mediaPlayer) {
50994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        stopPlaybackAtPosition(0, mDuration.get());
51094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
51194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
51294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private void stopPlaybackAtPosition(int clipPosition, int duration) {
51394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mPositionUpdater.stopUpdating();
51494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.playbackStopped();
51594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        if (mWakeLock.isHeld()) {
51694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mWakeLock.release();
51794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
51894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        // Always disable on stop.
51994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.disableProximitySensor();
52094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.setClipPosition(clipPosition, duration);
52194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        if (mPlayer.isPlaying()) {
52294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mPlayer.pause();
52394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
52494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
52594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
52694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private class PlaybackPositionListener implements SeekBar.OnSeekBarChangeListener {
52794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        private boolean mShouldResumePlaybackAfterSeeking;
52894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
52994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        @Override
53094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public void onStartTrackingTouch(SeekBar arg0) {
53194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            if (mPlayer.isPlaying()) {
53294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                mShouldResumePlaybackAfterSeeking = true;
53394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                stopPlaybackAtPosition(mPlayer.getCurrentPosition(), mDuration.get());
53494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            } else {
53594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                mShouldResumePlaybackAfterSeeking = false;
53694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            }
53794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
53894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
53994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        @Override
54094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public void onStopTrackingTouch(SeekBar arg0) {
54194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            if (mPlayer.isPlaying()) {
54294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                stopPlaybackAtPosition(mPlayer.getCurrentPosition(), mDuration.get());
54394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            }
54494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            if (mShouldResumePlaybackAfterSeeking) {
54594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                resetPrepareStartPlaying(mView.getDesiredClipPosition());
54694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            }
54794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
54894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
54994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        @Override
55094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
55194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mView.setClipPosition(seekBar.getProgress(), seekBar.getMax());
55294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
55394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
55494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
55594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private void changeRate(float rate, int stringResourceId) {
55694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        ((SingleThreadedMediaPlayerProxy) mPlayer).setVariableSpeed(rate);
55794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        mView.setRateDisplay(rate, stringResourceId);
55894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
55994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
56094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private class SpeakerphoneListener implements View.OnClickListener {
56194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        @Override
56294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public void onClick(View v) {
56394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            boolean previousState = mView.isSpeakerPhoneOn();
56494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mView.setSpeakerPhoneOn(!previousState);
56594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            if (mPlayer.isPlaying() && previousState) {
56694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                // If we are currently playing and we are disabling the speaker phone, enable the
56794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                // sensor.
56894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                mView.enableProximitySensor();
56994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            } else {
57094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                // If we are not currently playing, disable the sensor.
57194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                mView.disableProximitySensor();
57294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            }
57394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
57494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
57594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
57694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private class StartStopButtonListener implements View.OnClickListener {
57794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        @Override
57894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public void onClick(View arg0) {
57994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            if (mPlayer.isPlaying()) {
58094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                stopPlaybackAtPosition(mPlayer.getCurrentPosition(), mDuration.get());
58194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            } else {
58294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                resetPrepareStartPlaying(mView.getDesiredClipPosition());
58394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            }
58494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
58594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
58694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
58794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    /**
58894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     * Controls the animation of the playback slider.
58994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng     */
59094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    @ThreadSafe
59194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    private final class PositionUpdater implements Runnable {
59294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        private final ScheduledExecutorService mExecutorService;
59394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        private final int mPeriodMillis;
59494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        private final Object mLock = new Object();
59594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        @GuardedBy("mLock") private ScheduledFuture<?> mScheduledFuture;
59694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        private final Runnable mSetClipPostitionRunnable = new Runnable() {
59794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            @Override
59894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            public void run() {
59994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                int currentPosition = 0;
60094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                synchronized (mLock) {
60194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    if (mScheduledFuture == null) {
60294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                        // This task has been canceled. Just stop now.
60394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                        return;
60494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    }
60594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    currentPosition = mPlayer.getCurrentPosition();
60694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                }
60794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                mView.setClipPosition(currentPosition, mDuration.get());
60894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            }
60994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        };
61094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
61194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public PositionUpdater(ScheduledExecutorService executorService, int periodMillis) {
61294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mExecutorService = executorService;
61394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mPeriodMillis = periodMillis;
61494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
61594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
61694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        @Override
61794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public void run() {
61894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mView.runOnUiThread(mSetClipPostitionRunnable);
61994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
62094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
62194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public void startUpdating(int beginPosition, int endPosition) {
62294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            synchronized (mLock) {
62394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                if (mScheduledFuture != null) {
62494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    mScheduledFuture.cancel(false);
62556e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner                    mScheduledFuture = null;
62694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                }
62794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                mScheduledFuture = mExecutorService.scheduleAtFixedRate(this, 0, mPeriodMillis,
62894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                        TimeUnit.MILLISECONDS);
62994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            }
63094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
63194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
63294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        public void stopUpdating() {
63394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            synchronized (mLock) {
63494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                if (mScheduledFuture != null) {
63594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    mScheduledFuture.cancel(false);
63694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                    mScheduledFuture = null;
63794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng                }
63894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            }
63994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
64094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
64194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng
64294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    public void onPause() {
64394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        if (mPlayer.isPlaying()) {
64494b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            stopPlaybackAtPosition(mPlayer.getCurrentPosition(), mDuration.get());
64594b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
64694b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        if (mPrepareTask != null) {
64794b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mPrepareTask.cancel(false);
64856e34715b973f0eed3b4a0cec278ea412a89844dJay Shrauner            mPrepareTask = null;
64994b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
65094b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        if (mWakeLock.isHeld()) {
65194b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng            mWakeLock.release();
65294b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng        }
65394b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng    }
6542c1f343c68df2ce40b171e88f5660942053b1322Yorke Lee
6552c1f343c68df2ce40b171e88f5660942053b1322Yorke Lee    private static int constrain(int amount, int low, int high) {
6562c1f343c68df2ce40b171e88f5660942053b1322Yorke Lee        return amount < low ? low : (amount > high ? high : amount);
6572c1f343c68df2ce40b171e88f5660942053b1322Yorke Lee    }
65894b10b530c0fc297e2974e57e094c500d3ee6003Chiao Cheng}
659