1a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin/*
2a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * Copyright (C) 2017 The Android Open Source Project
3a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin *
4a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * Licensed under the Apache License, Version 2.0 (the "License");
5a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * you may not use this file except in compliance with the License.
6a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * You may obtain a copy of the License at
7a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin *
8a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin *      http://www.apache.org/licenses/LICENSE-2.0
9a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin *
10a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * Unless required by applicable law or agreed to in writing, software
11a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * distributed under the License is distributed on an "AS IS" BASIS,
12a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * See the License for the specific language governing permissions and
14a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * limitations under the License.
15a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin */
16a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
17a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinpackage android.support.v17.leanback.media;
18a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
19a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport android.content.Context;
20a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport android.graphics.drawable.Drawable;
21a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport android.support.annotation.CallSuper;
22a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport android.support.v17.leanback.widget.Action;
23a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport android.support.v17.leanback.widget.ArrayObjectAdapter;
24a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport android.support.v17.leanback.widget.ControlButtonPresenterSelector;
25a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport android.support.v17.leanback.widget.OnActionClickedListener;
26a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport android.support.v17.leanback.widget.PlaybackControlsRow;
27a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport android.support.v17.leanback.widget.PlaybackRowPresenter;
28a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport android.support.v17.leanback.widget.PlaybackTransportRowPresenter;
29a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport android.support.v17.leanback.widget.Presenter;
30a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport android.text.TextUtils;
31a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport android.util.Log;
32a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport android.view.KeyEvent;
33a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport android.view.View;
34a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
35a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinimport java.util.List;
36a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
37a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin/**
38a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * A base abstract class for managing a {@link PlaybackControlsRow} being displayed in
39a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * {@link PlaybackGlueHost}. It supports standard playback control actions play/pause and
40a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * skip next/previous. This helper class is a glue layer that manages interaction between the
41a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * leanback UI components {@link PlaybackControlsRow} {@link PlaybackRowPresenter}
42a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * and a functional {@link PlayerAdapter} which represents the underlying
43a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * media player.
44a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin *
45a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * <p>The app must pass a {@link PlayerAdapter} in constructor for a specific
46a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * implementation e.g. a {@link MediaPlayerAdapter}.
47a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * </p>
48a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin *
49a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * <p>The glue has two action bars: primary action bars and secondary action bars. Apps
50a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * can provide additional actions by overriding {@link #onCreatePrimaryActions} and / or
51a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * {@link #onCreateSecondaryActions} and respond to actions by overriding
52a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * {@link #onActionClicked(Action)}.
53a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * </p>
54a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin *
55a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * <p>The subclass is responsible for implementing the "repeat mode" in
56a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * {@link #onPlayCompleted()}.
57a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * </p>
58a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin *
59a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin * @param <T> Type of {@link PlayerAdapter} passed in constructor.
60a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin */
61a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsinpublic abstract class PlaybackBaseControlGlue<T extends PlayerAdapter> extends PlaybackGlue
62a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        implements OnActionClickedListener, View.OnKeyListener {
63a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
64a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    static final String TAG = "PlaybackTransportGlue";
65a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    static final boolean DEBUG = false;
66a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
67a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    final T mPlayerAdapter;
68a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    PlaybackControlsRow mControlsRow;
69a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    PlaybackRowPresenter mControlsRowPresenter;
70a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    PlaybackControlsRow.PlayPauseAction mPlayPauseAction;
71a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    boolean mIsPlaying = false;
72a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    boolean mFadeWhenPlaying = true;
73a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
74a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    CharSequence mSubtitle;
75a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    CharSequence mTitle;
76a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    Drawable mCover;
77a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
78a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    PlaybackGlueHost.PlayerCallback mPlayerCallback;
79a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    boolean mBuffering = false;
80a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    int mVideoWidth = 0;
81a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    int mVideoHeight = 0;
82a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    boolean mErrorSet = false;
83a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    int mErrorCode;
84a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    String mErrorMessage;
85a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
86a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    final PlayerAdapter.Callback mAdapterCallback = new PlayerAdapter
87a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            .Callback() {
88a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
89a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        @Override
90a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        public void onPlayStateChanged(PlayerAdapter wrapper) {
91a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            if (DEBUG) Log.v(TAG, "onPlayStateChanged");
92a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            PlaybackBaseControlGlue.this.onPlayStateChanged();
93a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
94a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
95a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        @Override
96a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        public void onCurrentPositionChanged(PlayerAdapter wrapper) {
97a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            if (DEBUG) Log.v(TAG, "onCurrentPositionChanged");
98a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            PlaybackBaseControlGlue.this.onUpdateProgress();
99a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
100a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
101a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        @Override
102a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        public void onBufferedPositionChanged(PlayerAdapter wrapper) {
103a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            if (DEBUG) Log.v(TAG, "onBufferedPositionChanged");
104a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            PlaybackBaseControlGlue.this.onUpdateBufferedProgress();
105a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
106a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
107a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        @Override
108a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        public void onDurationChanged(PlayerAdapter wrapper) {
109a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            if (DEBUG) Log.v(TAG, "onDurationChanged");
110a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            PlaybackBaseControlGlue.this.onUpdateDuration();
111a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
112a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
113a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        @Override
114a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        public void onPlayCompleted(PlayerAdapter wrapper) {
115a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            if (DEBUG) Log.v(TAG, "onPlayCompleted");
116a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            PlaybackBaseControlGlue.this.onPlayCompleted();
117a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
118a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
119a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        @Override
120a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        public void onPreparedStateChanged(PlayerAdapter wrapper) {
121a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            if (DEBUG) Log.v(TAG, "onPreparedStateChanged");
122a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            PlaybackBaseControlGlue.this.onPreparedStateChanged();
123a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
124a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
125a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        @Override
126a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        public void onVideoSizeChanged(PlayerAdapter wrapper, int width, int height) {
127a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            mVideoWidth = width;
128a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            mVideoHeight = height;
129a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            if (mPlayerCallback != null) {
130a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin                mPlayerCallback.onVideoSizeChanged(width, height);
131a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            }
132a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
133a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
134a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        @Override
135a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        public void onError(PlayerAdapter wrapper, int errorCode, String errorMessage) {
136a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            mErrorSet = true;
137a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            mErrorCode = errorCode;
138a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            mErrorMessage = errorMessage;
139a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            if (mPlayerCallback != null) {
140a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin                mPlayerCallback.onError(errorCode, errorMessage);
141a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            }
142a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
143a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
144a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        @Override
145a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        public void onBufferingStateChanged(PlayerAdapter wrapper, boolean start) {
146a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            mBuffering = start;
147a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            if (mPlayerCallback != null) {
148a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin                mPlayerCallback.onBufferingStateChanged(start);
149a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            }
150a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
151a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    };
152a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
153a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
154a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Constructor for the glue.
155a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     *
156a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * @param context
157a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * @param impl Implementation to underlying media player.
158a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
159a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public PlaybackBaseControlGlue(Context context, T impl) {
160a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        super(context);
161a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mPlayerAdapter = impl;
162a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mPlayerAdapter.setCallback(mAdapterCallback);
163a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
164a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
165a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public final T getPlayerAdapter() {
166a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        return mPlayerAdapter;
167a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
168a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
169a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    @Override
170a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    protected void onAttachedToHost(PlaybackGlueHost host) {
171a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        super.onAttachedToHost(host);
172a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        host.setOnKeyInterceptListener(this);
173a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        host.setOnActionClickedListener(this);
174a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        onCreateDefaultControlsRow();
175a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        onCreateDefaultRowPresenter();
176a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        host.setPlaybackRowPresenter(getPlaybackRowPresenter());
177a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        host.setPlaybackRow(getControlsRow());
178a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
179a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mPlayerCallback = host.getPlayerCallback();
180a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        onAttachHostCallback();
181a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mPlayerAdapter.onAttachedToHost(host);
182a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
183a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
184a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    void onAttachHostCallback() {
185a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (mPlayerCallback != null) {
186a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            if (mVideoWidth != 0 && mVideoHeight != 0) {
187a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin                mPlayerCallback.onVideoSizeChanged(mVideoWidth, mVideoHeight);
188a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            }
189a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            if (mErrorSet) {
190a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin                mPlayerCallback.onError(mErrorCode, mErrorMessage);
191a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            }
192a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            mPlayerCallback.onBufferingStateChanged(mBuffering);
193a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
194a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
195a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
196a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    void onDetachHostCallback() {
197a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mErrorSet = false;
198a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mErrorCode = 0;
199a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mErrorMessage = null;
200a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (mPlayerCallback != null) {
201a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            mPlayerCallback.onBufferingStateChanged(false);
202a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
203a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
204a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
205a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    @Override
206a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    protected void onHostStart() {
207a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mPlayerAdapter.setProgressUpdatingEnabled(true);
208a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
209a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
210a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    @Override
211a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    protected void onHostStop() {
212a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mPlayerAdapter.setProgressUpdatingEnabled(false);
213a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
214a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
215a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    @Override
216a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    protected void onDetachedFromHost() {
217a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        onDetachHostCallback();
218a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mPlayerCallback = null;
219a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mPlayerAdapter.onDetachedFromHost();
220a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mPlayerAdapter.setProgressUpdatingEnabled(false);
221a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        super.onDetachedFromHost();
222a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
223a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
224a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    void onCreateDefaultControlsRow() {
225a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (mControlsRow == null) {
226a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            PlaybackControlsRow controlsRow = new PlaybackControlsRow(this);
227a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            setControlsRow(controlsRow);
228a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
229a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
230a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
231a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    void onCreateDefaultRowPresenter() {
232a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (mControlsRowPresenter == null) {
233a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            setPlaybackRowPresenter(onCreateRowPresenter());
234a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
235a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
236a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
237a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    protected abstract PlaybackRowPresenter onCreateRowPresenter();
238a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
239a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
240a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Sets the controls to auto hide after a timeout when media is playing.
241a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * @param enable True to enable auto hide after a timeout when media is playing.
242a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * @see PlaybackGlueHost#setControlsOverlayAutoHideEnabled(boolean)
243a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
244a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public void setControlsOverlayAutoHideEnabled(boolean enable) {
245a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mFadeWhenPlaying = enable;
246a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (!mFadeWhenPlaying && getHost() != null) {
247a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            getHost().setControlsOverlayAutoHideEnabled(false);
248a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
249a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
250a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
251a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
252a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Returns true if the controls auto hides after a timeout when media is playing.
253a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * @see PlaybackGlueHost#isControlsOverlayAutoHideEnabled()
254a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
255a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public boolean isControlsOverlayAutoHideEnabled() {
256a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        return mFadeWhenPlaying;
257a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
258a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
259a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
260a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Sets the controls row to be managed by the glue layer. If
261a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * {@link PlaybackControlsRow#getPrimaryActionsAdapter()} is not provided, a default
262a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * {@link ArrayObjectAdapter} will be created and initialized in
263a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * {@link #onCreatePrimaryActions(ArrayObjectAdapter)}. If
264a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * {@link PlaybackControlsRow#getSecondaryActionsAdapter()} is not provided, a default
265a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * {@link ArrayObjectAdapter} will be created and initialized in
266a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * {@link #onCreateSecondaryActions(ArrayObjectAdapter)}.
267a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * The primary actions and playback state related aspects of the row
268a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * are updated by the glue.
269a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
270a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public void setControlsRow(PlaybackControlsRow controlsRow) {
271a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mControlsRow = controlsRow;
272a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mControlsRow.setCurrentPosition(-1);
273a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mControlsRow.setDuration(-1);
274a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mControlsRow.setBufferedPosition(-1);
275a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (mControlsRow.getPrimaryActionsAdapter() == null) {
276a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            ArrayObjectAdapter adapter = new ArrayObjectAdapter(
277a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin                    new ControlButtonPresenterSelector());
278a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            onCreatePrimaryActions(adapter);
279a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            mControlsRow.setPrimaryActionsAdapter(adapter);
280a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
281a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        // Add secondary actions
282a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (mControlsRow.getSecondaryActionsAdapter() == null) {
283a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            ArrayObjectAdapter secondaryActions = new ArrayObjectAdapter(
284a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin                    new ControlButtonPresenterSelector());
285a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            onCreateSecondaryActions(secondaryActions);
286a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            getControlsRow().setSecondaryActionsAdapter(secondaryActions);
287a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
288a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        updateControlsRow();
289a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
290a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
291a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
292a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Sets the controls row Presenter to be managed by the glue layer.
293a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
294a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {
295a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mControlsRowPresenter = presenter;
296a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
297a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
298a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
299a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Returns the playback controls row managed by the glue layer.
300a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
301a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public PlaybackControlsRow getControlsRow() {
302a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        return mControlsRow;
303a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
304a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
305a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
306a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Returns the playback controls row Presenter managed by the glue layer.
307a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
308a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public PlaybackRowPresenter getPlaybackRowPresenter() {
309a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        return mControlsRowPresenter;
310a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
311a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
312a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
313a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Handles action clicks.  A subclass may override this add support for additional actions.
314a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
315a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    @Override
316a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public abstract void onActionClicked(Action action);
317a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
318a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
319a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Handles key events and returns true if handled.  A subclass may override this to provide
320a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * additional support.
321a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
322a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    @Override
323a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public abstract boolean onKey(View v, int keyCode, KeyEvent event);
324a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
325a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    private void updateControlsRow() {
326a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        onMetadataChanged();
327a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
328a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
329a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    @Override
330a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public final boolean isPlaying() {
331a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        return mPlayerAdapter.isPlaying();
332a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
333a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
334a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    @Override
335a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public void play() {
336a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mPlayerAdapter.play();
337a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
338a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
339a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    @Override
340a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public void pause() {
341a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mPlayerAdapter.pause();
342a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
343a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
344a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    protected static void notifyItemChanged(ArrayObjectAdapter adapter, Object object) {
345a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        int index = adapter.indexOf(object);
346a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (index >= 0) {
347a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            adapter.notifyArrayItemRangeChanged(index, 1);
348a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
349a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
350a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
351a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
352a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * May be overridden to add primary actions to the adapter. Default implementation add
353a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * {@link PlaybackControlsRow.PlayPauseAction}.
354a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     *
355a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * @param primaryActionsAdapter The adapter to add primary {@link Action}s.
356a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
357a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    protected void onCreatePrimaryActions(ArrayObjectAdapter primaryActionsAdapter) {
358a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
359a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
360a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
361a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * May be overridden to add secondary actions to the adapter.
362a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     *
363a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * @param secondaryActionsAdapter The adapter you need to add the {@link Action}s to.
364a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
365a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    protected void onCreateSecondaryActions(ArrayObjectAdapter secondaryActionsAdapter) {
366a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
367a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
368a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    void onUpdateProgress() {
369a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (mControlsRow != null) {
370a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            mControlsRow.setCurrentPosition(mPlayerAdapter.isPrepared()
371a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin                    ? getCurrentPosition() : -1);
372a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
373a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
374a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
375a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    void onUpdateBufferedProgress() {
376a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (mControlsRow != null) {
377a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            mControlsRow.setBufferedPosition(mPlayerAdapter.getBufferedPosition());
378a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
379a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
380a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
381a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    void onUpdateDuration() {
382a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (mControlsRow != null) {
383a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            mControlsRow.setDuration(
384a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin                    mPlayerAdapter.isPrepared() ? mPlayerAdapter.getDuration() : -1);
385a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
386a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
387a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
388a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
389a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * @return The duration of the media item in milliseconds.
390a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
391a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public final long getDuration() {
392a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        return mPlayerAdapter.getDuration();
393a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
394a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
395a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
396a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * @return The current position of the media item in milliseconds.
397a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
398a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public long getCurrentPosition() {
399a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        return mPlayerAdapter.getCurrentPosition();
400a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
401a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
402a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
403a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * @return The current buffered position of the media item in milliseconds.
404a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
405a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public final long getBufferedPosition() {
406a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        return mPlayerAdapter.getBufferedPosition();
407a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
408a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
409a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    @Override
410a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public final boolean isPrepared() {
411a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        return mPlayerAdapter.isPrepared();
412a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
413a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
414a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
415a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Event when ready state for play changes.
416a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
417a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    @CallSuper
418a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    protected void onPreparedStateChanged() {
419a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        onUpdateDuration();
420a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        List<PlayerCallback> callbacks = getPlayerCallbacks();
421a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (callbacks != null) {
422a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            for (int i = 0, size = callbacks.size(); i < size; i++) {
423a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin                callbacks.get(i).onPreparedStateChanged(this);
424a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            }
425a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
426a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
427a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
428a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
429a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Sets the drawable representing cover image. The drawable will be rendered by default
430a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * description presenter in
431a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * {@link PlaybackTransportRowPresenter#setDescriptionPresenter(Presenter)}.
432a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * @param cover The drawable representing cover image.
433a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
434a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public void setArt(Drawable cover) {
435a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (mCover == cover) {
436a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            return;
437a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
438a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        this.mCover = cover;
439a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mControlsRow.setImageDrawable(mCover);
440a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (getHost() != null) {
441a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            getHost().notifyPlaybackRowChanged();
442a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
443a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
444a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
445a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
446a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * @return The drawable representing cover image.
447a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
448a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public Drawable getArt() {
449a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        return mCover;
450a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
451a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
452a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
453a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Sets the media subtitle. The subtitle will be rendered by default description presenter
454a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * {@link PlaybackTransportRowPresenter#setDescriptionPresenter(Presenter)}.
455a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * @param subtitle Subtitle to set.
456a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
457a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public void setSubtitle(CharSequence subtitle) {
458a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (TextUtils.equals(subtitle, mSubtitle)) {
459a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            return;
460a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
461a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mSubtitle = subtitle;
462a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (getHost() != null) {
463a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            getHost().notifyPlaybackRowChanged();
464a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
465a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
466a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
467a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
468a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Return The media subtitle.
469a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
470a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public CharSequence getSubtitle() {
471a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        return mSubtitle;
472a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
473a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
474a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
475a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Sets the media title. The title will be rendered by default description presenter
476a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * {@link PlaybackTransportRowPresenter#setDescriptionPresenter(Presenter)}.
477a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
478a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public void setTitle(CharSequence title) {
479a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (TextUtils.equals(title, mTitle)) {
480a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            return;
481a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
482a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mTitle = title;
483a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (getHost() != null) {
484a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            getHost().notifyPlaybackRowChanged();
485a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
486a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
487a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
488a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
489a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Returns the title of the media item.
490a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
491a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public CharSequence getTitle() {
492a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        return mTitle;
493a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
494a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
495a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
496a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Event when metadata changed
497a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
498a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    void onMetadataChanged() {
499a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (mControlsRow == null) {
500a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            return;
501a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
502a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
503a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (DEBUG) Log.v(TAG, "updateRowMetadata");
504a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
505a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mControlsRow.setImageDrawable(getArt());
506a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mControlsRow.setDuration(mPlayerAdapter.getDuration());
507a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mControlsRow.setCurrentPosition(getCurrentPosition());
508a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
509a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (getHost() != null) {
510a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            getHost().notifyPlaybackRowChanged();
511a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
512a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
513a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
514a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
515a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Event when play state changed.
516a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
517a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    @CallSuper
518a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    protected void onPlayStateChanged() {
519a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        List<PlayerCallback> callbacks = getPlayerCallbacks();
520a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (callbacks != null) {
521a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            for (int i = 0, size = callbacks.size(); i < size; i++) {
522a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin                callbacks.get(i).onPlayStateChanged(this);
523a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            }
524a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
525a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
526a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
527a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
528a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Event when play finishes, subclass may handling repeat mode here.
529a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
530a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    @CallSuper
531a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    protected void onPlayCompleted() {
532a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        List<PlayerCallback> callbacks = getPlayerCallbacks();
533a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        if (callbacks != null) {
534a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            for (int i = 0, size = callbacks.size(); i < size; i++) {
535a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin                callbacks.get(i).onPlayCompleted(this);
536a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin            }
537a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        }
538a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
539a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
540a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    /**
541a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * Seek media to a new position.
542a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     * @param position New position.
543a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin     */
544a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    public final void seekTo(long position) {
545a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin        mPlayerAdapter.seekTo(position);
546a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin    }
547a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin
548a4952dd9e1ddd678743f49d9d728b9b8c5fc7232Wei-Hsin}
549