1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/* 2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project 3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License"); 5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License. 6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at 7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * http://www.apache.org/licenses/LICENSE-2.0 9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software 11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS, 12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and 14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License. 15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.ui; 17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Context; 19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.res.TypedArray; 20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.graphics.Canvas; 21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.graphics.Path; 22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.graphics.RectF; 23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.media.AudioManager; 24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.media.MediaPlayer; 25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.media.MediaPlayer.OnCompletionListener; 26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.media.MediaPlayer.OnErrorListener; 27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.media.MediaPlayer.OnPreparedListener; 28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.net.Uri; 29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.SystemClock; 30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.text.TextUtils; 31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.util.AttributeSet; 32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.LayoutInflater; 33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.View; 34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.widget.ImageView; 35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.widget.LinearLayout; 36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.Factory; 38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.R; 39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.MessagePartData; 40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.mediapicker.PausableChronometer; 41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert; 42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.ContentType; 43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.LogUtil; 44eba0949bc60e9002adf0c96044677b4c281b2819Shri Bordeimport com.android.messaging.util.MediaUtil; 45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.UiUtils; 46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/** 48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * A reusable widget that hosts an audio player for audio attachment playback. This widget is used 49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * by both the media picker and the conversation message view to show audio attachments. 50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class AudioAttachmentView extends LinearLayout { 52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** The normal layout mode where we have the play button, timer and progress bar */ 53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final int LAYOUT_MODE_NORMAL = 0; 54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** The compact layout mode with only the play button and the timer beneath it. Suitable 56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * for displaying in limited space such as multi-attachment layout */ 57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final int LAYOUT_MODE_COMPACT = 1; 58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** The sub-compact layout mode with only the play button. */ 60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final int LAYOUT_MODE_SUB_COMPACT = 2; 61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final int PLAY_BUTTON = 0; 63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final int PAUSE_BUTTON = 1; 64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private AudioAttachmentPlayPauseButton mPlayPauseButton; 66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private PausableChronometer mChronometer; 67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private AudioPlaybackProgressBar mProgressBar; 68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private MediaPlayer mMediaPlayer; 69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private Uri mDataSourceUri; 71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // The corner radius for drawing rounded corners. The default value is zero (no rounded corners) 73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final int mCornerRadius; 74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final Path mRoundedCornerClipPath; 75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private int mClipPathWidth; 76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private int mClipPathHeight; 77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 78eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde private boolean mUseIncomingStyle; 79eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde private int mThemeColor; 80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 81eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde private boolean mStartPlayAfterPrepare; 82eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde // should the MediaPlayer be prepared lazily when the user chooses to play the audio (as 83eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde // opposed to preparing it early, on bind) 84eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde private boolean mPrepareOnPlayback; 85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private boolean mPrepared; 86eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde private boolean mPlaybackFinished; // Was the audio played all the way to the end 87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final int mMode; 88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public AudioAttachmentView(final Context context, final AttributeSet attrs) { 90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super(context, attrs); 91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final TypedArray typedAttributes = 92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd context.obtainStyledAttributes(attrs, R.styleable.AudioAttachmentView); 93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMode = typedAttributes.getInt(R.styleable.AudioAttachmentView_layoutMode, 94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LAYOUT_MODE_NORMAL); 95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final LayoutInflater inflater = LayoutInflater.from(getContext()); 96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd inflater.inflate(R.layout.audio_attachment_view, this, true); 97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd typedAttributes.recycle(); 98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setWillNotDraw(mMode != LAYOUT_MODE_SUB_COMPACT); 100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRoundedCornerClipPath = new Path(); 101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCornerRadius = context.getResources().getDimensionPixelSize( 102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd R.dimen.conversation_list_image_preview_corner_radius); 103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setContentDescription(context.getString(R.string.audio_attachment_content_description)); 104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd protected void onFinishInflate() { 108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.onFinishInflate(); 109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPlayPauseButton = (AudioAttachmentPlayPauseButton) findViewById(R.id.play_pause_button); 111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mChronometer = (PausableChronometer) findViewById(R.id.timer); 112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mProgressBar = (AudioPlaybackProgressBar) findViewById(R.id.progress); 113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPlayPauseButton.setOnClickListener(new OnClickListener() { 114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onClick(final View v) { 116eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde // Has the MediaPlayer already been prepared? 117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mMediaPlayer != null && mPrepared) { 118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mMediaPlayer.isPlaying()) { 119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMediaPlayer.pause(); 120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mChronometer.pause(); 121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mProgressBar.pause(); 122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd playAudio(); 124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 125eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde } else { 126eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde // Either eager preparation is still going on (the user must have clicked 127eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde // the Play button immediately after the view is bound) or this is lazy 128eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde // preparation. 129eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde if (mStartPlayAfterPrepare) { 130eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde // The user is (starting and) pausing before the MediaPlayer is prepared 131eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mStartPlayAfterPrepare = false; 132eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde } else { 133eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mStartPlayAfterPrepare = true; 134eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde setupMediaPlayer(); 135eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde } 136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updatePlayPauseButtonState(); 138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updatePlayPauseButtonState(); 141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd initializeViewsForMode(); 142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 144eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde private void updateChronometerVisibility(final boolean playing) { 145eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde if (mChronometer.getVisibility() == View.GONE) { 146eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde // The chronometer is always GONE for LAYOUT_MODE_SUB_COMPACT 147eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde Assert.equals(LAYOUT_MODE_SUB_COMPACT, mMode); 148eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde return; 149eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde } 150eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde 151eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde if (mPrepareOnPlayback) { 152eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde // For lazy preparation, the chronometer will only be shown during playback 153eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mChronometer.setVisibility(playing ? View.VISIBLE : View.INVISIBLE); 154eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde } else { 155eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mChronometer.setVisibility(View.VISIBLE); 156eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde } 157eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde } 158eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde 159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Bind the audio attachment view with a MessagePartData. 161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param incoming indicates whether the attachment view is to be styled as a part of an 162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * incoming message. 163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void bindMessagePartData(final MessagePartData messagePartData, 165eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde final boolean incoming, final boolean showAsSelected) { 166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(messagePartData == null || 167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ContentType.isAudioType(messagePartData.getContentType())); 168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri contentUri = (messagePartData == null) ? null : messagePartData.getContentUri(); 169eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde bind(contentUri, incoming, showAsSelected); 170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 172eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde public void bind( 173eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde final Uri dataSourceUri, final boolean incoming, final boolean showAsSelected) { 174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String currentUriString = (mDataSourceUri == null) ? "" : mDataSourceUri.toString(); 175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String newUriString = (dataSourceUri == null) ? "" : dataSourceUri.toString(); 176eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde final int themeColor = ConversationDrawables.get().getConversationThemeColor(); 177eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde final boolean useIncomingStyle = incoming || showAsSelected; 178eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde final boolean visualStyleChanged = mThemeColor != themeColor || 179eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mUseIncomingStyle != useIncomingStyle; 180eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde 181eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mUseIncomingStyle = useIncomingStyle; 182eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mThemeColor = themeColor; 183eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mPrepareOnPlayback = incoming && !MediaUtil.canAutoAccessIncomingMedia(); 184eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde 185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!TextUtils.equals(currentUriString, newUriString)) { 186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mDataSourceUri = dataSourceUri; 187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd resetToZeroState(); 188eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde } else if (visualStyleChanged) { 189eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde updateVisualStyle(); 190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void playAudio() { 194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(mMediaPlayer); 195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mPlaybackFinished) { 196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMediaPlayer.seekTo(0); 197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mChronometer.restart(); 198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mProgressBar.restart(); 199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPlaybackFinished = false; 200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mChronometer.resume(); 202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mProgressBar.resume(); 203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMediaPlayer.start(); 205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void onAudioReplayError(final int what, final int extra, final Exception exception) { 208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (exception == null) { 209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(LogUtil.BUGLE_TAG, "audio replay failed, what=" + what + 210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ", extra=" + extra); 211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(LogUtil.BUGLE_TAG, "audio replay failed, exception=" + exception); 213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UiUtils.showToastAtBottom(R.string.audio_recording_replay_failed); 215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd releaseMediaPlayer(); 216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 218eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde /** 219eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde * Prepare the MediaPlayer, and if mPrepareOnPlayback, start playing the audio 220eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde */ 221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void setupMediaPlayer() { 222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(mDataSourceUri); 223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mMediaPlayer == null) { 224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(!mPrepared); 225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMediaPlayer = new MediaPlayer(); 226eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde 227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMediaPlayer.setDataSource(Factory.get().getApplicationContext(), mDataSourceUri); 230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMediaPlayer.setOnCompletionListener(new OnCompletionListener() { 231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onCompletion(final MediaPlayer mp) { 233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updatePlayPauseButtonState(); 234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mChronometer.reset(); 235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mChronometer.setBase(SystemClock.elapsedRealtime() - 236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMediaPlayer.getDuration()); 237eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde updateChronometerVisibility(false /* playing */); 238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mProgressBar.reset(); 239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPlaybackFinished = true; 241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMediaPlayer.setOnPreparedListener(new OnPreparedListener() { 245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onPrepared(final MediaPlayer mp) { 247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Set base on the chronometer so we can show the full length of the audio. 248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mChronometer.setBase(SystemClock.elapsedRealtime() - 249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMediaPlayer.getDuration()); 250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mProgressBar.setDuration(mMediaPlayer.getDuration()); 251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMediaPlayer.seekTo(0); 252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPrepared = true; 253eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde 254eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde if (mStartPlayAfterPrepare) { 255eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mStartPlayAfterPrepare = false; 256eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde playAudio(); 257eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde updatePlayPauseButtonState(); 258eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde } 259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMediaPlayer.setOnErrorListener(new OnErrorListener() { 263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean onError(final MediaPlayer mp, final int what, final int extra) { 265eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mStartPlayAfterPrepare = false; 266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd onAudioReplayError(what, extra, null); 267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 270eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde 271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMediaPlayer.prepareAsync(); 272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final Exception exception) { 273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd onAudioReplayError(0, 0, exception); 274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd releaseMediaPlayer(); 275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void releaseMediaPlayer() { 280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mMediaPlayer != null) { 281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMediaPlayer.release(); 282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mMediaPlayer = null; 283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPrepared = false; 284eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mStartPlayAfterPrepare = false; 285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPlaybackFinished = false; 286eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mChronometer.reset(); 287eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mProgressBar.reset(); 288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd protected void onDetachedFromWindow() { 293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.onDetachedFromWindow(); 294eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde // The view must have scrolled off. Stop playback. 295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd releaseMediaPlayer(); 296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd protected void onDraw(final Canvas canvas) { 300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mMode != LAYOUT_MODE_SUB_COMPACT) { 301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int currentWidth = this.getWidth(); 305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int currentHeight = this.getHeight(); 306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mClipPathWidth != currentWidth || mClipPathHeight != currentHeight) { 307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final RectF rect = new RectF(0, 0, currentWidth, currentHeight); 308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRoundedCornerClipPath.reset(); 309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRoundedCornerClipPath.addRoundRect(rect, mCornerRadius, mCornerRadius, 310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Path.Direction.CW); 311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mClipPathWidth = currentWidth; 312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mClipPathHeight = currentHeight; 313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd canvas.clipPath(mRoundedCornerClipPath); 316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.onDraw(canvas); 317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void updatePlayPauseButtonState() { 320eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde final boolean playing = mMediaPlayer != null && mMediaPlayer.isPlaying(); 321eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde updateChronometerVisibility(playing); 322eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde if (mStartPlayAfterPrepare || playing) { 323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPlayPauseButton.setDisplayedChild(PAUSE_BUTTON); 324eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde } else { 325eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mPlayPauseButton.setDisplayedChild(PLAY_BUTTON); 326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void resetToZeroState() { 330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Release the media player so it may be set up with the new audio source. 331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd releaseMediaPlayer(); 332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateVisualStyle(); 333eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde updateChronometerVisibility(false /* playing */); 334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 335eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde if (mDataSourceUri != null && !mPrepareOnPlayback) { 336eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde // Prepare the media player, so we can read the duration of the audio. 337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setupMediaPlayer(); 338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void updateVisualStyle() { 342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mMode == LAYOUT_MODE_SUB_COMPACT) { 343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Sub-compact mode has static visual appearance already set up during initialization. 344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 347eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde if (mUseIncomingStyle) { 348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mChronometer.setTextColor(getResources().getColor(R.color.message_text_color_incoming)); 349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mChronometer.setTextColor(getResources().getColor(R.color.message_text_color_outgoing)); 351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 352eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mProgressBar.setVisualStyle(mUseIncomingStyle); 353eba0949bc60e9002adf0c96044677b4c281b2819Shri Borde mPlayPauseButton.setVisualStyle(mUseIncomingStyle); 354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updatePlayPauseButtonState(); 355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void initializeViewsForMode() { 358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd switch (mMode) { 359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case LAYOUT_MODE_NORMAL: 360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setOrientation(HORIZONTAL); 361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mProgressBar.setVisibility(VISIBLE); 362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case LAYOUT_MODE_COMPACT: 365d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setOrientation(VERTICAL); 366d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mProgressBar.setVisibility(GONE); 367d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ((MarginLayoutParams) mPlayPauseButton.getLayoutParams()).setMargins(0, 0, 0, 0); 368d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ((MarginLayoutParams) mChronometer.getLayoutParams()).setMargins(0, 0, 0, 0); 369d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 370d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 371d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case LAYOUT_MODE_SUB_COMPACT: 372d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setOrientation(VERTICAL); 373d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mProgressBar.setVisibility(GONE); 374d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mChronometer.setVisibility(GONE); 375d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ((MarginLayoutParams) mPlayPauseButton.getLayoutParams()).setMargins(0, 0, 0, 0); 376d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ImageView playButton = (ImageView) findViewById(R.id.play_button); 377d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd playButton.setImageDrawable( 378d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getResources().getDrawable(R.drawable.ic_preview_play)); 379d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ImageView pauseButton = (ImageView) findViewById(R.id.pause_button); 380d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd pauseButton.setImageDrawable( 381d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getResources().getDrawable(R.drawable.ic_preview_pause)); 382d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 383d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 384d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd default: 385d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.fail("Unsupported mode for AudioAttachmentView!"); 386d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 387d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 388d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 389d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd} 390