1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.dialer.voicemail.listui; 18 19 import android.content.Context; 20 import android.media.AudioManager; 21 import android.media.MediaPlayer; 22 import android.media.MediaPlayer.OnCompletionListener; 23 import android.media.MediaPlayer.OnErrorListener; 24 import android.media.MediaPlayer.OnPreparedListener; 25 import android.net.Uri; 26 import android.support.annotation.NonNull; 27 import android.support.annotation.Nullable; 28 import com.android.dialer.common.Assert; 29 import com.android.dialer.common.LogUtil; 30 import com.android.dialer.strictmode.StrictModeUtils; 31 import java.io.IOException; 32 33 /** A wrapper around {@link MediaPlayer} */ 34 public class NewVoicemailMediaPlayer { 35 36 private final MediaPlayer mediaPlayer; 37 private Uri voicemailLastPlayedOrPlayingUri; 38 private Uri voicemailUriLastPreparedOrPreparingToPlay; 39 40 private OnErrorListener newVoicemailMediaPlayerOnErrorListener; 41 private OnPreparedListener newVoicemailMediaPlayerOnPreparedListener; 42 private OnCompletionListener newVoicemailMediaPlayerOnCompletionListener; 43 private Uri pausedUri; 44 @Nullable private Uri voicemailRequestedToDownload; 45 NewVoicemailMediaPlayer(@onNull MediaPlayer player)46 public NewVoicemailMediaPlayer(@NonNull MediaPlayer player) { 47 mediaPlayer = Assert.isNotNull(player); 48 } 49 50 // TODO(uabdullah): Consider removing the StrictModeUtils.bypass (a bug) prepareMediaPlayerAndPlayVoicemailWhenReady(Context context, Uri uri)51 public void prepareMediaPlayerAndPlayVoicemailWhenReady(Context context, Uri uri) 52 throws IOException { 53 Assert.checkArgument(uri != null, "Media player cannot play a null uri"); 54 LogUtil.i( 55 "NewVoicemailMediaPlayer", 56 "trying to prepare playing voicemail uri: %s", 57 String.valueOf(uri)); 58 try { 59 reset(); 60 voicemailUriLastPreparedOrPreparingToPlay = uri; 61 verifyListenersNotNull(); 62 LogUtil.i("NewVoicemailMediaPlayer", "setData source"); 63 StrictModeUtils.bypass( 64 () -> { 65 try { 66 mediaPlayer.setDataSource(context, uri); 67 setAudioManagerToNonSpeakerMode(context); 68 } catch (IOException e) { 69 LogUtil.i( 70 "NewVoicemailMediaPlayer", 71 "threw an Exception when setting datasource " 72 + e 73 + " for uri: " 74 + uri 75 + "for context : " 76 + context); 77 } 78 }); 79 LogUtil.i("NewVoicemailMediaPlayer", "prepare async"); 80 StrictModeUtils.bypass(() -> mediaPlayer.prepareAsync()); 81 } catch (IllegalStateException e) { 82 LogUtil.i( 83 "NewVoicemailMediaPlayer", "caught an IllegalStateException state exception : \n" + e); 84 } catch (Exception e) { 85 LogUtil.i( 86 "NewVoicemailMediaPlayer", 87 "threw an Exception " + e + " for uri: " + uri + "for context : " + context); 88 } 89 } 90 91 /** We should never start playing voicemails from the speaker mode */ setAudioManagerToNonSpeakerMode(Context context)92 private void setAudioManagerToNonSpeakerMode(Context context) { 93 AudioManager audioManager = context.getSystemService(AudioManager.class); 94 audioManager.setMode(AudioManager.STREAM_MUSIC); 95 audioManager.setSpeakerphoneOn(false); 96 } 97 verifyListenersNotNull()98 private void verifyListenersNotNull() { 99 Assert.isNotNull( 100 newVoicemailMediaPlayerOnErrorListener, 101 "newVoicemailMediaPlayerOnErrorListener must be set before preparing to " 102 + "play voicemails"); 103 Assert.isNotNull( 104 newVoicemailMediaPlayerOnCompletionListener, 105 "newVoicemailMediaPlayerOnCompletionListener must be set before preparing" 106 + " to play voicemails"); 107 Assert.isNotNull( 108 newVoicemailMediaPlayerOnPreparedListener, 109 "newVoicemailMediaPlayerOnPreparedListener must be set before preparing to" 110 + " play voicemails"); 111 } 112 113 // Must be called from onPrepared start(Uri startPlayingVoicemailUri)114 public void start(Uri startPlayingVoicemailUri) { 115 Assert.checkArgument( 116 startPlayingVoicemailUri.equals(voicemailUriLastPreparedOrPreparingToPlay), 117 "uri:%s was not prepared before calling start. Uri that is currently prepared: %s", 118 startPlayingVoicemailUri, 119 getLastPreparedOrPreparingToPlayVoicemailUri()); 120 121 mediaPlayer.start(); 122 voicemailLastPlayedOrPlayingUri = startPlayingVoicemailUri; 123 pausedUri = null; 124 voicemailRequestedToDownload = null; 125 } 126 reset()127 public void reset() { 128 LogUtil.enterBlock("NewVoicemailMediaPlayer.reset"); 129 mediaPlayer.reset(); 130 voicemailLastPlayedOrPlayingUri = null; 131 voicemailUriLastPreparedOrPreparingToPlay = null; 132 pausedUri = null; 133 voicemailRequestedToDownload = null; 134 } 135 pauseMediaPlayer(Uri voicemailUri)136 public void pauseMediaPlayer(Uri voicemailUri) { 137 pausedUri = voicemailUri; 138 Assert.checkArgument( 139 voicemailUriLastPreparedOrPreparingToPlay.equals(voicemailLastPlayedOrPlayingUri), 140 "last prepared and last playing should be the same"); 141 Assert.checkArgument( 142 pausedUri.equals(voicemailLastPlayedOrPlayingUri), 143 "only the last played uri can be paused"); 144 mediaPlayer.pause(); 145 } 146 seekTo(int progress)147 public void seekTo(int progress) { 148 mediaPlayer.seekTo(progress); 149 } 150 setOnErrorListener(OnErrorListener onErrorListener)151 public void setOnErrorListener(OnErrorListener onErrorListener) { 152 mediaPlayer.setOnErrorListener(onErrorListener); 153 newVoicemailMediaPlayerOnErrorListener = onErrorListener; 154 } 155 setOnPreparedListener(OnPreparedListener onPreparedListener)156 public void setOnPreparedListener(OnPreparedListener onPreparedListener) { 157 mediaPlayer.setOnPreparedListener(onPreparedListener); 158 newVoicemailMediaPlayerOnPreparedListener = onPreparedListener; 159 } 160 setOnCompletionListener(OnCompletionListener onCompletionListener)161 public void setOnCompletionListener(OnCompletionListener onCompletionListener) { 162 mediaPlayer.setOnCompletionListener(onCompletionListener); 163 newVoicemailMediaPlayerOnCompletionListener = onCompletionListener; 164 } 165 setVoicemailRequestedToDownload(@onNull Uri uri)166 public void setVoicemailRequestedToDownload(@NonNull Uri uri) { 167 Assert.isNotNull(uri, "cannot download a null voicemail"); 168 voicemailRequestedToDownload = uri; 169 } 170 171 /** 172 * Note: In some cases it's possible mediaPlayer.isPlaying() can return true, but 173 * mediaPlayer.getCurrentPosition() can be greater than mediaPlayer.getDuration(), after which 174 * mediaPlayer.isPlaying() will be false. This is a weird corner case and adding the 175 * mediaPlayer.getCurrentPosition() < mediaPlayer.getDuration() check here messes with the 176 * mediaPlayer.start() (doesn't return mediaPlayer.isPlaying() to be true immediately). 177 * 178 * @return if the media plaer; 179 */ isPlaying()180 public boolean isPlaying() { 181 return mediaPlayer.isPlaying(); 182 } 183 getCurrentPosition()184 public int getCurrentPosition() { 185 return mediaPlayer.getCurrentPosition(); 186 } 187 getLastPlayedOrPlayingVoicemailUri()188 public Uri getLastPlayedOrPlayingVoicemailUri() { 189 if (mediaPlayer.isPlaying()) { 190 Assert.isNotNull(voicemailLastPlayedOrPlayingUri); 191 } 192 193 return voicemailLastPlayedOrPlayingUri == null ? Uri.EMPTY : voicemailLastPlayedOrPlayingUri; 194 } 195 196 /** 197 * All the places that call this function, we expect the voicemail to have been prepared, but we 198 * could get rid of the assert check in the future if needed. 199 */ getLastPreparedOrPreparingToPlayVoicemailUri()200 public Uri getLastPreparedOrPreparingToPlayVoicemailUri() { 201 return Assert.isNotNull( 202 voicemailUriLastPreparedOrPreparingToPlay, 203 "we expect whoever called this to have prepared a voicemail before calling this function"); 204 } 205 getLastPausedVoicemailUri()206 public Uri getLastPausedVoicemailUri() { 207 return pausedUri; 208 } 209 getMediaPlayer()210 public MediaPlayer getMediaPlayer() { 211 return mediaPlayer; 212 } 213 getDuration()214 public int getDuration() { 215 Assert.checkArgument(mediaPlayer != null); 216 return mediaPlayer.getDuration(); 217 } 218 219 /** 220 * A null v/s non-value is important for the {@link NewVoicemailAdapter} to differentiate between 221 * a underlying table change due to a voicemail being downloaded or something else (e.g delete). 222 * 223 * @return if there was a Uri that was requested to be downloaded from the server, null otherwise. 224 */ 225 @Nullable getVoicemailRequestedToDownload()226 public Uri getVoicemailRequestedToDownload() { 227 return voicemailRequestedToDownload; 228 } 229 isPaused()230 public boolean isPaused() { 231 return pausedUri != null; 232 } 233 } 234