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