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