Home | History | Annotate | Download | only in impl_with_mp1
      1 /*
      2  * Copyright 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.media.widget;
     18 
     19 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
     20 
     21 import android.content.Context;
     22 import android.content.pm.ActivityInfo;
     23 import android.content.res.Resources;
     24 import android.graphics.Bitmap;
     25 import android.graphics.BitmapFactory;
     26 import android.graphics.Point;
     27 import android.graphics.drawable.BitmapDrawable;
     28 import android.graphics.drawable.Drawable;
     29 import android.media.AudioAttributes;
     30 import android.media.AudioFocusRequest;
     31 import android.media.AudioManager;
     32 import android.media.MediaMetadataRetriever;
     33 import android.media.MediaPlayer;
     34 import android.media.PlaybackParams;
     35 import android.net.Uri;
     36 import android.os.Bundle;
     37 import android.os.ResultReceiver;
     38 import android.support.v4.media.MediaMetadataCompat;
     39 import android.support.v4.media.session.MediaControllerCompat;
     40 import android.support.v4.media.session.MediaControllerCompat.PlaybackInfo;
     41 import android.support.v4.media.session.MediaSessionCompat;
     42 import android.support.v4.media.session.PlaybackStateCompat;
     43 import android.util.AttributeSet;
     44 import android.util.DisplayMetrics;
     45 import android.util.Log;
     46 import android.util.Pair;
     47 import android.view.LayoutInflater;
     48 import android.view.MotionEvent;
     49 import android.view.View;
     50 import android.view.ViewGroup.LayoutParams;
     51 import android.view.WindowManager;
     52 import android.view.accessibility.AccessibilityManager;
     53 import android.widget.ImageView;
     54 import android.widget.TextView;
     55 
     56 import androidx.annotation.NonNull;
     57 import androidx.annotation.Nullable;
     58 import androidx.annotation.RequiresApi;
     59 import androidx.annotation.RestrictTo;
     60 import androidx.media.AudioAttributesCompat;
     61 import androidx.media.DataSourceDesc;
     62 import androidx.media.MediaItem2;
     63 import androidx.media.MediaMetadata2;
     64 import androidx.media.SessionToken2;
     65 import androidx.mediarouter.media.MediaControlIntent;
     66 import androidx.mediarouter.media.MediaItemStatus;
     67 import androidx.mediarouter.media.MediaRouteSelector;
     68 import androidx.mediarouter.media.MediaRouter;
     69 import androidx.palette.graphics.Palette;
     70 
     71 import java.io.IOException;
     72 import java.util.ArrayList;
     73 import java.util.List;
     74 import java.util.Map;
     75 import java.util.concurrent.Executor;
     76 
     77 /**
     78  * Base implementation of VideoView2.
     79  */
     80 @RequiresApi(21) // TODO correct minSdk API use incompatibilities and remove before release.
     81 class VideoView2ImplBaseWithMp1
     82         implements VideoView2Impl, VideoViewInterfaceWithMp1.SurfaceListener {
     83     private static final String TAG = "VideoView2ImplBase_1";
     84     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     85     private static final long DEFAULT_SHOW_CONTROLLER_INTERVAL_MS = 2000;
     86 
     87     private static final int STATE_ERROR = -1;
     88     private static final int STATE_IDLE = 0;
     89     private static final int STATE_PREPARING = 1;
     90     private static final int STATE_PREPARED = 2;
     91     private static final int STATE_PLAYING = 3;
     92     private static final int STATE_PAUSED = 4;
     93     private static final int STATE_PLAYBACK_COMPLETED = 5;
     94 
     95     private static final int INVALID_TRACK_INDEX = -1;
     96     private static final float INVALID_SPEED = 0f;
     97 
     98     private static final int SIZE_TYPE_EMBEDDED = 0;
     99     private static final int SIZE_TYPE_FULL = 1;
    100     private static final int SIZE_TYPE_MINIMAL = 2;
    101 
    102     private AccessibilityManager mAccessibilityManager;
    103     private AudioManager mAudioManager;
    104     private AudioAttributes mAudioAttributes;
    105     private int mAudioFocusType = AudioManager.AUDIOFOCUS_GAIN; // legacy focus gain
    106     private boolean mAudioFocused = false;
    107 
    108     private Pair<Executor, VideoView2.OnCustomActionListener> mCustomActionListenerRecord;
    109     private VideoView2.OnViewTypeChangedListener mViewTypeChangedListener;
    110 
    111     private VideoViewInterfaceWithMp1 mCurrentView;
    112     private VideoTextureViewWithMp1 mTextureView;
    113     private VideoSurfaceViewWithMp1 mSurfaceView;
    114 
    115     protected MediaPlayer mMediaPlayer;
    116     private DataSourceDesc mDsd;
    117     private Uri mUri;
    118     private Map<String, String> mHeaders;
    119     private MediaControlView2 mMediaControlView;
    120     protected MediaSessionCompat mMediaSession;
    121     private MediaControllerCompat mMediaController;
    122     private MediaMetadata2 mMediaMetadata;
    123     private MediaMetadataRetriever mRetriever;
    124     private boolean mNeedUpdateMediaType;
    125     private Bundle mMediaTypeData;
    126     private String mTitle;
    127 
    128     private WindowManager mManager;
    129     private Resources mResources;
    130     private View mMusicView;
    131     private Drawable mMusicAlbumDrawable;
    132     private String mMusicTitleText;
    133     private String mMusicArtistText;
    134     private int mPrevWidth;
    135     private int mPrevHeight;
    136     private int mDominantColor;
    137     private int mSizeType;
    138 
    139     private PlaybackStateCompat.Builder mStateBuilder;
    140     private List<PlaybackStateCompat.CustomAction> mCustomActionList;
    141 
    142     private int mTargetState = STATE_IDLE;
    143     private int mCurrentState = STATE_IDLE;
    144     private int mCurrentBufferPercentage;
    145     private long mSeekWhenPrepared;  // recording the seek position while preparing
    146 
    147     private int mVideoWidth;
    148     private int mVideoHeight;
    149 
    150     protected ArrayList<Integer> mVideoTrackIndices;
    151     protected ArrayList<Integer> mAudioTrackIndices;
    152 
    153     // selected video/audio/subtitle track index as MediaPlayer returns
    154     protected int mSelectedVideoTrackIndex;
    155     protected int mSelectedAudioTrackIndex;
    156 
    157     private float mSpeed;
    158     private float mFallbackSpeed;  // keep the original speed before 'pause' is called.
    159     private float mVolumeLevelFloat;
    160     private int mVolumeLevel;
    161     protected VideoView2 mInstance;
    162 
    163     private long mShowControllerIntervalMs;
    164 
    165     private MediaRouter mMediaRouter;
    166     private MediaRouteSelector mRouteSelector;
    167     private MediaRouter.RouteInfo mRoute;
    168     private RoutePlayer mRoutePlayer;
    169 
    170     private final MediaRouter.Callback mRouterCallback = new MediaRouter.Callback() {
    171         @Override
    172         public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
    173             if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
    174                 // Stop local playback (if necessary)
    175                 resetPlayer();
    176                 mRoute = route;
    177                 mRoutePlayer = new RoutePlayer(mInstance.getContext(), route);
    178                 mRoutePlayer.setPlayerEventCallback(new RoutePlayer.PlayerEventCallback() {
    179                     @Override
    180                     public void onPlayerStateChanged(MediaItemStatus itemStatus) {
    181                         PlaybackStateCompat.Builder psBuilder = new PlaybackStateCompat.Builder();
    182                         psBuilder.setActions(RoutePlayer.PLAYBACK_ACTIONS);
    183                         long position = itemStatus.getContentPosition();
    184                         switch (itemStatus.getPlaybackState()) {
    185                             case MediaItemStatus.PLAYBACK_STATE_PENDING:
    186                                 psBuilder.setState(PlaybackStateCompat.STATE_NONE, position, 0);
    187                                 mCurrentState = STATE_IDLE;
    188                                 break;
    189                             case MediaItemStatus.PLAYBACK_STATE_PLAYING:
    190                                 psBuilder.setState(PlaybackStateCompat.STATE_PLAYING, position, 1);
    191                                 mCurrentState = STATE_PLAYING;
    192                                 break;
    193                             case MediaItemStatus.PLAYBACK_STATE_PAUSED:
    194                                 psBuilder.setState(PlaybackStateCompat.STATE_PAUSED, position, 0);
    195                                 mCurrentState = STATE_PAUSED;
    196                                 break;
    197                             case MediaItemStatus.PLAYBACK_STATE_BUFFERING:
    198                                 psBuilder.setState(
    199                                         PlaybackStateCompat.STATE_BUFFERING, position, 0);
    200                                 mCurrentState = STATE_PAUSED;
    201                                 break;
    202                             case MediaItemStatus.PLAYBACK_STATE_FINISHED:
    203                                 psBuilder.setState(PlaybackStateCompat.STATE_STOPPED, position, 0);
    204                                 mCurrentState = STATE_PLAYBACK_COMPLETED;
    205                                 break;
    206                         }
    207 
    208                         PlaybackStateCompat pbState = psBuilder.build();
    209                         mMediaSession.setPlaybackState(pbState);
    210 
    211                         MediaMetadataCompat.Builder mmBuilder = new MediaMetadataCompat.Builder();
    212                         mmBuilder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION,
    213                                 itemStatus.getContentDuration());
    214                         mMediaSession.setMetadata(mmBuilder.build());
    215                     }
    216                 });
    217                 // Start remote playback (if necessary)
    218                 // TODO: b/77556429
    219                 mRoutePlayer.openVideo(mUri);
    220             }
    221         }
    222 
    223         @Override
    224         public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route, int reason) {
    225             if (mRoute != null && mRoutePlayer != null) {
    226                 mRoutePlayer.release();
    227                 mRoutePlayer = null;
    228             }
    229             if (mRoute == route) {
    230                 mRoute = null;
    231             }
    232             if (reason != MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) {
    233                 // TODO: Resume local playback  (if necessary)
    234                 // TODO: b/77556429
    235                 openVideo(mUri, mHeaders);
    236             }
    237         }
    238     };
    239 
    240     @Override
    241     public void initialize(
    242             VideoView2 instance, Context context,
    243             @Nullable AttributeSet attrs, int defStyleAttr) {
    244         mInstance = instance;
    245 
    246         mVideoWidth = 0;
    247         mVideoHeight = 0;
    248         mSpeed = 1.0f;
    249         mFallbackSpeed = mSpeed;
    250         mShowControllerIntervalMs = DEFAULT_SHOW_CONTROLLER_INTERVAL_MS;
    251 
    252         mAccessibilityManager = (AccessibilityManager) context.getSystemService(
    253                 Context.ACCESSIBILITY_SERVICE);
    254 
    255         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    256         mAudioAttributes = new AudioAttributes.Builder()
    257                 .setUsage(AudioAttributes.USAGE_MEDIA)
    258                 .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE).build();
    259 
    260         mInstance.setFocusable(true);
    261         mInstance.setFocusableInTouchMode(true);
    262         mInstance.requestFocus();
    263 
    264         mTextureView = new VideoTextureViewWithMp1(context);
    265         mSurfaceView = new VideoSurfaceViewWithMp1(context);
    266         LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
    267                 LayoutParams.MATCH_PARENT);
    268         mTextureView.setLayoutParams(params);
    269         mSurfaceView.setLayoutParams(params);
    270         mTextureView.setSurfaceListener(this);
    271         mSurfaceView.setSurfaceListener(this);
    272 
    273         mInstance.addView(mTextureView);
    274         mInstance.addView(mSurfaceView);
    275 
    276         boolean enableControlView = (attrs == null) || attrs.getAttributeBooleanValue(
    277                 "http://schemas.android.com/apk/res/android",
    278                 "enableControlView", true);
    279         if (enableControlView) {
    280             mMediaControlView = new MediaControlView2(context);
    281         }
    282 
    283         // Choose surface view by default
    284         int viewType = (attrs == null) ? VideoView2.VIEW_TYPE_SURFACEVIEW
    285                 : attrs.getAttributeIntValue(
    286                 "http://schemas.android.com/apk/res/android",
    287                 "viewType", VideoView2.VIEW_TYPE_SURFACEVIEW);
    288         if (viewType == VideoView2.VIEW_TYPE_SURFACEVIEW) {
    289             Log.d(TAG, "viewType attribute is surfaceView.");
    290             mTextureView.setVisibility(View.GONE);
    291             mSurfaceView.setVisibility(View.VISIBLE);
    292             mCurrentView = mSurfaceView;
    293         } else if (viewType == VideoView2.VIEW_TYPE_TEXTUREVIEW) {
    294             Log.d(TAG, "viewType attribute is textureView.");
    295             mTextureView.setVisibility(View.VISIBLE);
    296             mSurfaceView.setVisibility(View.GONE);
    297             mCurrentView = mTextureView;
    298         }
    299 
    300         MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder();
    301         builder.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
    302         builder.addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO);
    303         builder.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
    304         mRouteSelector = builder.build();
    305     }
    306 
    307     /**
    308      * Sets MediaControlView2 instance. It will replace the previously assigned MediaControlView2
    309      * instance if any.
    310      *
    311      * @param mediaControlView a media control view2 instance.
    312      * @param intervalMs a time interval in milliseconds until VideoView2 hides MediaControlView2.
    313      */
    314     @Override
    315     public void setMediaControlView2(MediaControlView2 mediaControlView, long intervalMs) {
    316         mMediaControlView = mediaControlView;
    317         mShowControllerIntervalMs = intervalMs;
    318         mMediaControlView.setRouteSelector(mRouteSelector);
    319 
    320         if (mInstance.isAttachedToWindow()) {
    321             attachMediaControlView();
    322         }
    323     }
    324 
    325     /**
    326      * Returns MediaControlView2 instance which is currently attached to VideoView2 by default or by
    327      * {@link #setMediaControlView2} method.
    328      */
    329     @Override
    330     public MediaControlView2 getMediaControlView2() {
    331         return mMediaControlView;
    332     }
    333 
    334     /**
    335      * Sets MediaMetadata2 instance. It will replace the previously assigned MediaMetadata2 instance
    336      * if any.
    337      *
    338      * @param metadata a MediaMetadata2 instance.
    339      * @hide
    340      */
    341     @RestrictTo(LIBRARY_GROUP)
    342     @Override
    343     public void setMediaMetadata(MediaMetadata2 metadata) {
    344       //mProvider.setMediaMetadata_impl(metadata);
    345     }
    346 
    347     /**
    348      * Returns MediaMetadata2 instance which is retrieved from MediaPlayer inside VideoView2 by
    349      * default or by {@link #setMediaMetadata} method.
    350      * @hide
    351      */
    352     @RestrictTo(LIBRARY_GROUP)
    353     @Override
    354     public MediaMetadata2 getMediaMetadata() {
    355         return mMediaMetadata;
    356     }
    357 
    358     /**
    359      * Returns MediaController instance which is connected with MediaSession that VideoView2 is
    360      * using. This method should be called when VideoView2 is attached to window, or it throws
    361      * IllegalStateException, since internal MediaSession instance is not available until
    362      * this view is attached to window. Please check {@link View#isAttachedToWindow}
    363      * before calling this method.
    364      *
    365      * @throws IllegalStateException if interal MediaSession is not created yet.
    366      * @hide  TODO: remove
    367      */
    368     @RestrictTo(LIBRARY_GROUP)
    369     @Override
    370     public MediaControllerCompat getMediaController() {
    371         if (mMediaSession == null) {
    372             throw new IllegalStateException("MediaSession instance is not available.");
    373         }
    374         return mMediaController;
    375     }
    376 
    377     /**
    378      * Returns {@link SessionToken2} so that developers create their own
    379      * {@link androidx.media.MediaController2} instance. This method should be called when
    380      * VideoView2 is attached to window, or it throws IllegalStateException.
    381      *
    382      * @throws IllegalStateException if interal MediaSession is not created yet.
    383      * @hide
    384      */
    385     @RestrictTo(LIBRARY_GROUP)
    386     @Override
    387     public SessionToken2 getMediaSessionToken() {
    388         //return mProvider.getMediaSessionToken_impl();
    389         return null;
    390     }
    391 
    392     /**
    393      * Shows or hides closed caption or subtitles if there is any.
    394      * The first subtitle track will be chosen if there multiple subtitle tracks exist.
    395      * Default behavior of VideoView2 is not showing subtitle.
    396      * @param enable shows closed caption or subtitles if this value is true, or hides.
    397      */
    398     @Override
    399     public void setSubtitleEnabled(boolean enable) {
    400         // No-op on API < 28
    401     }
    402 
    403     /**
    404      * Returns true if showing subtitle feature is enabled or returns false.
    405      * Although there is no subtitle track or closed caption, it can return true, if the feature
    406      * has been enabled by {@link #setSubtitleEnabled}.
    407      */
    408     @Override
    409     public boolean isSubtitleEnabled() {
    410         // Not supported on API < 28
    411         return false;
    412     }
    413 
    414     /**
    415      * Sets playback speed.
    416      *
    417      * It is expressed as a multiplicative factor, where normal speed is 1.0f. If it is less than
    418      * or equal to zero, it will be just ignored and nothing will be changed. If it exceeds the
    419      * maximum speed that internal engine supports, system will determine best handling or it will
    420      * be reset to the normal speed 1.0f.
    421      * @param speed the playback speed. It should be positive.
    422      */
    423     @Override
    424     public void setSpeed(float speed) {
    425         if (speed <= 0.0f) {
    426             Log.e(TAG, "Unsupported speed (" + speed + ") is ignored.");
    427             return;
    428         }
    429         mSpeed = speed;
    430         if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
    431             applySpeed();
    432         }
    433         updatePlaybackState();
    434     }
    435 
    436     /**
    437      * Returns playback speed.
    438      *
    439      * It returns the same value that has been set by {@link #setSpeed}, if it was available value.
    440      * If {@link #setSpeed} has not been called before, then the normal speed 1.0f will be returned.
    441      */
    442     @Override
    443     public float getSpeed() {
    444         return mSpeed;
    445     }
    446 
    447     /**
    448      * Sets which type of audio focus will be requested during the playback, or configures playback
    449      * to not request audio focus. Valid values for focus requests are
    450      * {@link AudioManager#AUDIOFOCUS_GAIN}, {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT},
    451      * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, and
    452      * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}. Or use
    453      * {@link AudioManager#AUDIOFOCUS_NONE} to express that audio focus should not be
    454      * requested when playback starts. You can for instance use this when playing a silent animation
    455      * through this class, and you don't want to affect other audio applications playing in the
    456      * background.
    457      *
    458      * @param focusGain the type of audio focus gain that will be requested, or
    459      *                  {@link AudioManager#AUDIOFOCUS_NONE} to disable the use audio focus during
    460      *                  playback.
    461      */
    462     @Override
    463     public void setAudioFocusRequest(int focusGain) {
    464         if (focusGain != AudioManager.AUDIOFOCUS_NONE
    465                 && focusGain != AudioManager.AUDIOFOCUS_GAIN
    466                 && focusGain != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
    467                 && focusGain != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
    468                 && focusGain != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) {
    469             throw new IllegalArgumentException("Illegal audio focus type " + focusGain);
    470         }
    471         mAudioFocusType = focusGain;
    472     }
    473 
    474     /**
    475      * Sets the {@link AudioAttributesCompat} to be used during the playback of the video.
    476      *
    477      * @param attributes non-null <code>AudioAttributesCompat</code>.
    478      */
    479     @Override
    480     public void setAudioAttributes(@NonNull AudioAttributesCompat attributes) {
    481         if (attributes == null) {
    482             throw new IllegalArgumentException("Illegal null AudioAttributesCompat");
    483         }
    484         mAudioAttributes = (AudioAttributes) attributes.unwrap();
    485     }
    486 
    487     /**
    488      * Sets video path.
    489      *
    490      * @param path the path of the video.
    491      *
    492      * @hide
    493      */
    494     @RestrictTo(LIBRARY_GROUP)
    495     @Override
    496     public void setVideoPath(String path) {
    497         setVideoUri(Uri.parse(path));
    498     }
    499 
    500     /**
    501      * Sets video URI.
    502      *
    503      * @param uri the URI of the video.
    504      *
    505      * @hide
    506      */
    507     @RestrictTo(LIBRARY_GROUP)
    508     @Override
    509     public void setVideoUri(Uri uri) {
    510         setVideoUri(uri, null);
    511     }
    512 
    513     /**
    514      * Sets video URI using specific headers.
    515      *
    516      * @param uri     the URI of the video.
    517      * @param headers the headers for the URI request.
    518      *                Note that the cross domain redirection is allowed by default, but that can be
    519      *                changed with key/value pairs through the headers parameter with
    520      *                "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
    521      *                to disallow or allow cross domain redirection.
    522      */
    523     @Override
    524     public void setVideoUri(Uri uri, @Nullable Map<String, String> headers) {
    525         mSeekWhenPrepared = 0;
    526         openVideo(uri, headers);
    527     }
    528 
    529     /**
    530      * Sets {@link MediaItem2} object to render using VideoView2. Alternative way to set media
    531      * object to VideoView2 is {@link #setDataSource}.
    532      * @param mediaItem the MediaItem2 to play
    533      * @see #setDataSource
    534      *
    535      * @hide
    536      */
    537     @RestrictTo(LIBRARY_GROUP)
    538     @Override
    539     public void setMediaItem(@NonNull MediaItem2 mediaItem) {
    540     }
    541 
    542     /**
    543      * Sets {@link DataSourceDesc} object to render using VideoView2.
    544      * @param dataSource the {@link DataSourceDesc} object to play.
    545      * @see #setMediaItem
    546      * @hide
    547      */
    548     @RestrictTo(LIBRARY_GROUP)
    549     @Override
    550     public void setDataSource(@NonNull DataSourceDesc dataSource) {
    551     }
    552 
    553     /**
    554      * Selects which view will be used to render video between SurfacView and TextureView.
    555      *
    556      * @param viewType the view type to render video
    557      * <ul>
    558      * <li>{@link #VideoView2.VIEW_TYPE_SURFACEVIEW}
    559      * <li>{@link #VideoView2.VIEW_TYPE_TEXTUREVIEW}
    560      * </ul>
    561      */
    562     @Override
    563     public void setViewType(@VideoView2.ViewType int viewType) {
    564         if (viewType == mCurrentView.getViewType()) {
    565             return;
    566         }
    567         VideoViewInterfaceWithMp1 targetView;
    568         if (viewType == VideoView2.VIEW_TYPE_TEXTUREVIEW) {
    569             Log.d(TAG, "switching to TextureView");
    570             targetView = mTextureView;
    571         } else if (viewType == VideoView2.VIEW_TYPE_SURFACEVIEW) {
    572             Log.d(TAG, "switching to SurfaceView");
    573             targetView = mSurfaceView;
    574         } else {
    575             throw new IllegalArgumentException("Unknown view type: " + viewType);
    576         }
    577         ((View) targetView).setVisibility(View.VISIBLE);
    578         targetView.takeOver(mCurrentView);
    579         mInstance.requestLayout();
    580     }
    581 
    582     /**
    583      * Returns view type.
    584      *
    585      * @return view type. See {@see setViewType}.
    586      */
    587     @Override
    588     @VideoView2.ViewType
    589     public int getViewType() {
    590         return mCurrentView.getViewType();
    591     }
    592 
    593     /**
    594      * Sets custom actions which will be shown as custom buttons in {@link MediaControlView2}.
    595      *
    596      * @param actionList A list of {@link PlaybackStateCompat.CustomAction}. The return value of
    597      *                   {@link PlaybackStateCompat.CustomAction#getIcon()} will be used to draw
    598      *                   buttons in {@link MediaControlView2}.
    599      * @param executor executor to run callbacks on.
    600      * @param listener A listener to be called when a custom button is clicked.
    601      * @hide
    602      */
    603     @RestrictTo(LIBRARY_GROUP)
    604     @Override
    605     public void setCustomActions(List<PlaybackStateCompat.CustomAction> actionList,
    606             Executor executor, VideoView2.OnCustomActionListener listener) {
    607         mCustomActionList = actionList;
    608         mCustomActionListenerRecord = new Pair<>(executor, listener);
    609 
    610         // Create a new playback builder in order to clear existing the custom actions.
    611         mStateBuilder = null;
    612         updatePlaybackState();
    613     }
    614 
    615     /**
    616      * Registers a callback to be invoked when a view type change is done.
    617      * {@see #setViewType(int)}
    618      * @param l The callback that will be run
    619      * @hide
    620      */
    621     @RestrictTo(LIBRARY_GROUP)
    622     @Override
    623     public void setOnViewTypeChangedListener(VideoView2.OnViewTypeChangedListener l) {
    624         mViewTypeChangedListener = l;
    625     }
    626 
    627     @Override
    628     public void onAttachedToWindowImpl() {
    629         // Create MediaSession
    630         mMediaSession = new MediaSessionCompat(mInstance.getContext(), "VideoView2MediaSession");
    631         mMediaSession.setCallback(new MediaSessionCallback());
    632         mMediaSession.setActive(true);
    633         mMediaController = mMediaSession.getController();
    634         attachMediaControlView();
    635         if (mCurrentState == STATE_PREPARED) {
    636             extractTracks();
    637             extractMetadata();
    638             extractAudioMetadata();
    639             if (mNeedUpdateMediaType) {
    640                 mMediaSession.sendSessionEvent(
    641                         MediaControlView2.EVENT_UPDATE_MEDIA_TYPE_STATUS,
    642                         mMediaTypeData);
    643                 mNeedUpdateMediaType = false;
    644             }
    645         }
    646 
    647         mMediaRouter = MediaRouter.getInstance(mInstance.getContext());
    648         mMediaRouter.setMediaSessionCompat(mMediaSession);
    649         mMediaRouter.addCallback(mRouteSelector, mRouterCallback,
    650                 MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
    651     }
    652 
    653     @Override
    654     public void onDetachedFromWindowImpl() {
    655         mMediaSession.release();
    656         mMediaSession = null;
    657         mMediaController = null;
    658     }
    659 
    660     @Override
    661     public void onTouchEventImpl(MotionEvent ev) {
    662         if (DEBUG) {
    663             Log.d(TAG, "onTouchEvent(). mCurrentState=" + mCurrentState
    664                     + ", mTargetState=" + mTargetState);
    665         }
    666         if (ev.getAction() == MotionEvent.ACTION_UP && mMediaControlView != null) {
    667             if (!isMusicMediaType() || mSizeType != SIZE_TYPE_FULL) {
    668                 toggleMediaControlViewVisibility();
    669             }
    670         }
    671     }
    672 
    673     @Override
    674     public void onTrackballEventImpl(MotionEvent ev) {
    675         if (ev.getAction() == MotionEvent.ACTION_UP && mMediaControlView != null) {
    676             if (!isMusicMediaType() || mSizeType != SIZE_TYPE_FULL) {
    677                 toggleMediaControlViewVisibility();
    678             }
    679         }
    680     }
    681 
    682     @Override
    683     public void onMeasureImpl(int widthMeasureSpec, int heightMeasureSpec) {
    684         if (isMusicMediaType()) {
    685             int currWidth = mInstance.getMeasuredWidth();
    686             int currHeight = mInstance.getMeasuredHeight();
    687             if (mPrevWidth != currWidth || mPrevHeight != currHeight) {
    688                 Point screenSize = new Point();
    689                 mManager.getDefaultDisplay().getSize(screenSize);
    690                 int screenWidth = screenSize.x;
    691                 int screenHeight = screenSize.y;
    692 
    693                 if (currWidth == screenWidth && currHeight == screenHeight) {
    694                     int orientation = retrieveOrientation();
    695                     if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
    696                         inflateMusicView(R.layout.full_landscape_music);
    697                     } else {
    698                         inflateMusicView(R.layout.full_portrait_music);
    699                     }
    700 
    701                     if (mSizeType != SIZE_TYPE_FULL) {
    702                         mSizeType = SIZE_TYPE_FULL;
    703                         // Remove existing mFadeOut callback
    704                         mMediaControlView.removeCallbacks(mFadeOut);
    705                         mMediaControlView.setVisibility(View.VISIBLE);
    706                     }
    707                 } else {
    708                     if (mSizeType != SIZE_TYPE_EMBEDDED) {
    709                         mSizeType = SIZE_TYPE_EMBEDDED;
    710                         inflateMusicView(R.layout.embedded_music);
    711                         // Add new mFadeOut callback
    712                         mMediaControlView.postDelayed(mFadeOut, mShowControllerIntervalMs);
    713                     }
    714                 }
    715                 mPrevWidth = currWidth;
    716                 mPrevHeight = currHeight;
    717             }
    718         }
    719     }
    720 
    721     ///////////////////////////////////////////////////
    722     // Implements VideoViewInterfaceWithMp1.SurfaceListener
    723     ///////////////////////////////////////////////////
    724 
    725     @Override
    726     public void onSurfaceCreated(View view, int width, int height) {
    727         if (DEBUG) {
    728             Log.d(TAG, "onSurfaceCreated(). mCurrentState=" + mCurrentState
    729                     + ", mTargetState=" + mTargetState + ", width/height: " + width + "/" + height
    730                     + ", " + view.toString());
    731         }
    732         if (needToStart()) {
    733             mMediaController.getTransportControls().play();
    734         }
    735     }
    736 
    737     @Override
    738     public void onSurfaceDestroyed(View view) {
    739         if (DEBUG) {
    740             Log.d(TAG, "onSurfaceDestroyed(). mCurrentState=" + mCurrentState
    741                     + ", mTargetState=" + mTargetState + ", " + view.toString());
    742         }
    743     }
    744 
    745     @Override
    746     public void onSurfaceChanged(View view, int width, int height) {
    747         if (DEBUG) {
    748             Log.d(TAG, "onSurfaceChanged(). width/height: " + width + "/" + height
    749                     + ", " + view.toString());
    750         }
    751     }
    752 
    753     @Override
    754     public void onSurfaceTakeOverDone(VideoViewInterfaceWithMp1 view) {
    755         if (DEBUG) {
    756             Log.d(TAG, "onSurfaceTakeOverDone(). Now current view is: " + view);
    757         }
    758         mCurrentView = view;
    759         if (mViewTypeChangedListener != null) {
    760             mViewTypeChangedListener.onViewTypeChanged(mInstance, view.getViewType());
    761         }
    762         if (needToStart()) {
    763             mMediaController.getTransportControls().play();
    764         }
    765     }
    766 
    767     ///////////////////////////////////////////////////
    768     // Protected or private methods
    769     ///////////////////////////////////////////////////
    770 
    771     private void attachMediaControlView() {
    772         // Get MediaController from MediaSession and set it inside MediaControlView
    773         mMediaControlView.setController(mMediaSession.getController());
    774 
    775         LayoutParams params =
    776                 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    777         mInstance.addView(mMediaControlView, params);
    778     }
    779 
    780     protected boolean isInPlaybackState() {
    781         return (mMediaPlayer != null || mRoutePlayer != null)
    782                 && mCurrentState != STATE_ERROR
    783                 && mCurrentState != STATE_IDLE
    784                 && mCurrentState != STATE_PREPARING;
    785     }
    786 
    787     private boolean needToStart() {
    788         return (mMediaPlayer != null || mRoutePlayer != null)
    789                 && isAudioGranted()
    790                 && isWaitingPlayback();
    791     }
    792 
    793     private boolean isMusicMediaType() {
    794         return mVideoTrackIndices != null && mVideoTrackIndices.size() == 0;
    795     }
    796 
    797     private boolean isWaitingPlayback() {
    798         return mCurrentState != STATE_PLAYING && mTargetState == STATE_PLAYING;
    799     }
    800 
    801     private boolean isAudioGranted() {
    802         return mAudioFocused || mAudioFocusType == AudioManager.AUDIOFOCUS_NONE;
    803     }
    804 
    805     private AudioManager.OnAudioFocusChangeListener mAudioFocusListener =
    806             new AudioManager.OnAudioFocusChangeListener() {
    807         @Override
    808         public void onAudioFocusChange(int focusChange) {
    809             switch (focusChange) {
    810                 case AudioManager.AUDIOFOCUS_GAIN:
    811                     mAudioFocused = true;
    812                     if (needToStart()) {
    813                         mMediaController.getTransportControls().play();
    814                     }
    815                     break;
    816                 case AudioManager.AUDIOFOCUS_LOSS:
    817                 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
    818                 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
    819                     mAudioFocused = false;
    820                     if (isInPlaybackState() && mMediaPlayer.isPlaying()) {
    821                         mMediaController.getTransportControls().pause();
    822                     } else {
    823                         mTargetState = STATE_PAUSED;
    824                     }
    825             }
    826         }
    827     };
    828 
    829     @SuppressWarnings("deprecation")
    830     private void requestAudioFocus(int focusType) {
    831         int result;
    832         if (android.os.Build.VERSION.SDK_INT >= 26) {
    833             AudioFocusRequest focusRequest;
    834             focusRequest = new AudioFocusRequest.Builder(focusType)
    835                     .setAudioAttributes(mAudioAttributes)
    836                     .setOnAudioFocusChangeListener(mAudioFocusListener)
    837                     .build();
    838             result = mAudioManager.requestAudioFocus(focusRequest);
    839         } else {
    840             result = mAudioManager.requestAudioFocus(mAudioFocusListener,
    841                     AudioManager.STREAM_MUSIC,
    842                     focusType);
    843         }
    844         if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
    845             mAudioFocused = false;
    846         } else if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    847             mAudioFocused = true;
    848         } else if (result == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
    849             mAudioFocused = false;
    850         }
    851     }
    852 
    853     // Creates a MediaPlayer instance and prepare playback.
    854     private void openVideo(Uri uri, Map<String, String> headers) {
    855         resetPlayer();
    856         mUri = uri;
    857         if (isRemotePlayback()) {
    858             // TODO: b/77556429
    859             mRoutePlayer.openVideo(uri);
    860             return;
    861         }
    862 
    863         try {
    864             Log.d(TAG, "openVideo(): creating new MediaPlayer instance.");
    865             mMediaPlayer = new MediaPlayer();
    866             final Context context = mInstance.getContext();
    867             setupMediaPlayer(context, uri, headers);
    868 
    869             // we don't set the target state here either, but preserve the
    870             // target state that was there before.
    871             mCurrentState = STATE_PREPARING;
    872             mMediaPlayer.prepareAsync();
    873 
    874             // Save file name as title since the file may not have a title Metadata.
    875             mTitle = uri.getPath();
    876             String scheme = uri.getScheme();
    877             if (scheme != null && scheme.equals("file")) {
    878                 mTitle = uri.getLastPathSegment();
    879                 mRetriever = new MediaMetadataRetriever();
    880                 mRetriever.setDataSource(context, uri);
    881             }
    882 
    883             if (DEBUG) {
    884                 Log.d(TAG, "openVideo(). mCurrentState=" + mCurrentState
    885                         + ", mTargetState=" + mTargetState);
    886             }
    887         } catch (IOException | IllegalArgumentException ex) {
    888             Log.w(TAG, "Unable to open content: " + uri, ex);
    889             mCurrentState = STATE_ERROR;
    890             mTargetState = STATE_ERROR;
    891             mErrorListener.onError(mMediaPlayer,
    892                     MediaPlayer.MEDIA_ERROR_UNKNOWN, MediaPlayer.MEDIA_ERROR_IO);
    893         }
    894     }
    895 
    896     /**
    897      * Used in openVideo(). Setup MediaPlayer and related objects before calling prepare.
    898      */
    899     protected void setupMediaPlayer(Context context, Uri uri, Map<String, String> headers)
    900             throws IOException {
    901         mSurfaceView.setMediaPlayer(mMediaPlayer);
    902         mTextureView.setMediaPlayer(mMediaPlayer);
    903         mCurrentView.assignSurfaceToMediaPlayer(mMediaPlayer);
    904 
    905         mMediaPlayer.setOnPreparedListener(mPreparedListener);
    906         mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
    907         mMediaPlayer.setOnCompletionListener(mCompletionListener);
    908         mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener);
    909         mMediaPlayer.setOnErrorListener(mErrorListener);
    910         mMediaPlayer.setOnInfoListener(mInfoListener);
    911         mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
    912 
    913         mCurrentBufferPercentage = -1;
    914         mMediaPlayer.setDataSource(context, uri, headers);
    915         mMediaPlayer.setAudioAttributes(mAudioAttributes);
    916     }
    917 
    918     /*
    919      * Reset the media player in any state
    920      */
    921     @SuppressWarnings("deprecation")
    922     private void resetPlayer() {
    923         if (mMediaPlayer != null) {
    924             mMediaPlayer.reset();
    925             mMediaPlayer.release();
    926             mMediaPlayer = null;
    927             mTextureView.setMediaPlayer(null);
    928             mSurfaceView.setMediaPlayer(null);
    929             mCurrentState = STATE_IDLE;
    930             mTargetState = STATE_IDLE;
    931             if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
    932                 mAudioManager.abandonAudioFocus(null);
    933             }
    934         }
    935         mVideoWidth = 0;
    936         mVideoHeight = 0;
    937     }
    938 
    939     private void updatePlaybackState() {
    940         if (mStateBuilder == null) {
    941             long playbackActions = PlaybackStateCompat.ACTION_PLAY
    942                     | PlaybackStateCompat.ACTION_PAUSE
    943                     | PlaybackStateCompat.ACTION_REWIND | PlaybackStateCompat.ACTION_FAST_FORWARD
    944                     | PlaybackStateCompat.ACTION_SEEK_TO;
    945             mStateBuilder = new PlaybackStateCompat.Builder();
    946             mStateBuilder.setActions(playbackActions);
    947 
    948             if (mCustomActionList != null) {
    949                 for (PlaybackStateCompat.CustomAction action : mCustomActionList) {
    950                     mStateBuilder.addCustomAction(action);
    951                 }
    952             }
    953         }
    954         mStateBuilder.setState(getCorrespondingPlaybackState(),
    955                 mMediaPlayer.getCurrentPosition(), mSpeed);
    956         if (mCurrentState != STATE_ERROR
    957                 && mCurrentState != STATE_IDLE
    958                 && mCurrentState != STATE_PREPARING) {
    959             if (mCurrentBufferPercentage == -1) {
    960                 mStateBuilder.setBufferedPosition(-1);
    961             } else {
    962                 mStateBuilder.setBufferedPosition(
    963                         (long) (mCurrentBufferPercentage / 100.0 * mMediaPlayer.getDuration()));
    964             }
    965         }
    966 
    967         // Set PlaybackState for MediaSession
    968         if (mMediaSession != null) {
    969             PlaybackStateCompat state = mStateBuilder.build();
    970             mMediaSession.setPlaybackState(state);
    971         }
    972     }
    973 
    974     private int getCorrespondingPlaybackState() {
    975         switch (mCurrentState) {
    976             case STATE_ERROR:
    977                 return PlaybackStateCompat.STATE_ERROR;
    978             case STATE_IDLE:
    979                 return PlaybackStateCompat.STATE_NONE;
    980             case STATE_PREPARING:
    981                 return PlaybackStateCompat.STATE_CONNECTING;
    982             case STATE_PREPARED:
    983                 return PlaybackStateCompat.STATE_PAUSED;
    984             case STATE_PLAYING:
    985                 return PlaybackStateCompat.STATE_PLAYING;
    986             case STATE_PAUSED:
    987                 return PlaybackStateCompat.STATE_PAUSED;
    988             case STATE_PLAYBACK_COMPLETED:
    989                 return PlaybackStateCompat.STATE_STOPPED;
    990             default:
    991                 return -1;
    992         }
    993     }
    994 
    995     private final Runnable mFadeOut = new Runnable() {
    996         @Override
    997         public void run() {
    998             if (mCurrentState == STATE_PLAYING) {
    999                 mMediaControlView.setVisibility(View.GONE);
   1000             }
   1001         }
   1002     };
   1003 
   1004     private void showController() {
   1005         if (mMediaControlView == null || !isInPlaybackState()
   1006                 || (isMusicMediaType() && mSizeType == SIZE_TYPE_FULL)) {
   1007             return;
   1008         }
   1009         mMediaControlView.removeCallbacks(mFadeOut);
   1010         mMediaControlView.setVisibility(View.VISIBLE);
   1011         if (mShowControllerIntervalMs != 0
   1012                 && !mAccessibilityManager.isTouchExplorationEnabled()) {
   1013             mMediaControlView.postDelayed(mFadeOut, mShowControllerIntervalMs);
   1014         }
   1015     }
   1016 
   1017     private void toggleMediaControlViewVisibility() {
   1018         if (mMediaControlView.getVisibility() == View.VISIBLE) {
   1019             mMediaControlView.removeCallbacks(mFadeOut);
   1020             mMediaControlView.setVisibility(View.GONE);
   1021         } else {
   1022             showController();
   1023         }
   1024     }
   1025 
   1026     private void applySpeed() {
   1027         if (android.os.Build.VERSION.SDK_INT < 23) {
   1028             return;
   1029         }
   1030         PlaybackParams params = mMediaPlayer.getPlaybackParams().allowDefaults();
   1031         if (mSpeed != params.getSpeed()) {
   1032             try {
   1033                 params.setSpeed(mSpeed);
   1034                 mMediaPlayer.setPlaybackParams(params);
   1035                 mFallbackSpeed = mSpeed;
   1036             } catch (IllegalArgumentException e) {
   1037                 Log.e(TAG, "PlaybackParams has unsupported value: " + e);
   1038                 float fallbackSpeed = mMediaPlayer.getPlaybackParams().allowDefaults().getSpeed();
   1039                 if (fallbackSpeed > 0.0f) {
   1040                     mFallbackSpeed = fallbackSpeed;
   1041                 }
   1042                 mSpeed = mFallbackSpeed;
   1043             }
   1044         }
   1045     }
   1046 
   1047     private boolean isRemotePlayback() {
   1048         if (mMediaController == null) {
   1049             return false;
   1050         }
   1051         PlaybackInfo playbackInfo = mMediaController.getPlaybackInfo();
   1052         return playbackInfo != null
   1053                 && playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE;
   1054     }
   1055 
   1056     protected void extractTracks() {
   1057         MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
   1058         mVideoTrackIndices = new ArrayList<>();
   1059         mAudioTrackIndices = new ArrayList<>();
   1060         for (int i = 0; i < trackInfos.length; ++i) {
   1061             int trackType = trackInfos[i].getTrackType();
   1062             if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_VIDEO) {
   1063                 mVideoTrackIndices.add(i);
   1064             } else if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_AUDIO) {
   1065                 mAudioTrackIndices.add(i);
   1066             }
   1067         }
   1068         // Select first tracks as default
   1069         if (mVideoTrackIndices.size() > 0) {
   1070             mSelectedVideoTrackIndex = 0;
   1071         }
   1072         if (mAudioTrackIndices.size() > 0) {
   1073             mSelectedAudioTrackIndex = 0;
   1074         }
   1075 
   1076         Bundle data = new Bundle();
   1077         data.putInt(MediaControlView2.KEY_VIDEO_TRACK_COUNT, mVideoTrackIndices.size());
   1078         data.putInt(MediaControlView2.KEY_AUDIO_TRACK_COUNT, mAudioTrackIndices.size());
   1079         mMediaSession.sendSessionEvent(MediaControlView2.EVENT_UPDATE_TRACK_STATUS, data);
   1080     }
   1081 
   1082     protected void doShowSubtitleCommand(Bundle args) {
   1083         // No-op
   1084     }
   1085 
   1086     protected void doHideSubtitleCommand() {
   1087         // No-op
   1088     }
   1089 
   1090     private void extractMetadata() {
   1091         if (mRetriever == null) {
   1092             return;
   1093         }
   1094         // Get and set duration and title values as MediaMetadata for MediaControlView2
   1095         MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
   1096         String title = mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
   1097         if (title != null) {
   1098             mTitle = title;
   1099         }
   1100         builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, mTitle);
   1101         builder.putLong(
   1102                 MediaMetadataCompat.METADATA_KEY_DURATION, mMediaPlayer.getDuration());
   1103 
   1104         if (mMediaSession != null) {
   1105             mMediaSession.setMetadata(builder.build());
   1106         }
   1107     }
   1108 
   1109     @SuppressWarnings("deprecation")
   1110     private void extractAudioMetadata() {
   1111         if (mRetriever == null || !isMusicMediaType()) {
   1112             return;
   1113         }
   1114 
   1115         mResources = mInstance.getResources();
   1116         mManager = (WindowManager) mInstance.getContext().getApplicationContext()
   1117                 .getSystemService(Context.WINDOW_SERVICE);
   1118 
   1119         byte[] album = mRetriever.getEmbeddedPicture();
   1120         if (album != null) {
   1121             Bitmap bitmap = BitmapFactory.decodeByteArray(album, 0, album.length);
   1122             mMusicAlbumDrawable = new BitmapDrawable(bitmap);
   1123 
   1124             Palette.Builder builder = Palette.from(bitmap);
   1125             builder.generate(new Palette.PaletteAsyncListener() {
   1126                 @Override
   1127                 public void onGenerated(Palette palette) {
   1128                     mDominantColor = palette.getDominantColor(0);
   1129                     if (mMusicView != null) {
   1130                         mMusicView.setBackgroundColor(mDominantColor);
   1131                     }
   1132                 }
   1133             });
   1134         } else {
   1135             mMusicAlbumDrawable = mResources.getDrawable(R.drawable.ic_default_album_image);
   1136         }
   1137 
   1138         String title = mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
   1139         if (title != null) {
   1140             mMusicTitleText = title;
   1141         } else {
   1142             mMusicTitleText = mResources.getString(R.string.mcv2_music_title_unknown_text);
   1143         }
   1144 
   1145         String artist = mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
   1146         if (artist != null) {
   1147             mMusicArtistText = artist;
   1148         } else {
   1149             mMusicArtistText = mResources.getString(R.string.mcv2_music_artist_unknown_text);
   1150         }
   1151 
   1152         // Send title and artist string to MediaControlView2
   1153         MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
   1154         builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, mMusicTitleText);
   1155         builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, mMusicArtistText);
   1156         mMediaSession.setMetadata(builder.build());
   1157 
   1158         // Display Embedded mode as default
   1159         mInstance.removeView(mSurfaceView);
   1160         mInstance.removeView(mTextureView);
   1161         inflateMusicView(R.layout.embedded_music);
   1162     }
   1163 
   1164     private int retrieveOrientation() {
   1165         DisplayMetrics dm = Resources.getSystem().getDisplayMetrics();
   1166         int width = dm.widthPixels;
   1167         int height = dm.heightPixels;
   1168 
   1169         return (height > width)
   1170                 ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
   1171                 : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
   1172     }
   1173 
   1174     private void inflateMusicView(int layoutId) {
   1175         mInstance.removeView(mMusicView);
   1176 
   1177         LayoutInflater inflater = (LayoutInflater) mInstance.getContext()
   1178                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   1179         View v = inflater.inflate(layoutId, null);
   1180         v.setBackgroundColor(mDominantColor);
   1181 
   1182         ImageView albumView = v.findViewById(R.id.album);
   1183         if (albumView != null) {
   1184             albumView.setImageDrawable(mMusicAlbumDrawable);
   1185         }
   1186 
   1187         TextView titleView = v.findViewById(R.id.title);
   1188         if (titleView != null) {
   1189             titleView.setText(mMusicTitleText);
   1190         }
   1191 
   1192         TextView artistView = v.findViewById(R.id.artist);
   1193         if (artistView != null) {
   1194             artistView.setText(mMusicArtistText);
   1195         }
   1196 
   1197         mMusicView = v;
   1198         mInstance.addView(mMusicView, 0);
   1199     }
   1200 
   1201     private MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
   1202             new MediaPlayer.OnVideoSizeChangedListener() {
   1203                 @Override
   1204                 public void onVideoSizeChanged(
   1205                         MediaPlayer mp, int width, int height) {
   1206                     if (DEBUG) {
   1207                         Log.d(TAG, "onVideoSizeChanged(): size: " + width + "/" + height);
   1208                     }
   1209                     mVideoWidth = mp.getVideoWidth();
   1210                     mVideoHeight = mp.getVideoHeight();
   1211                     if (DEBUG) {
   1212                         Log.d(TAG, "onVideoSizeChanged(): mVideoSize:" + mVideoWidth + "/"
   1213                                 + mVideoHeight);
   1214                     }
   1215                     if (mVideoWidth != 0 && mVideoHeight != 0) {
   1216                         mInstance.requestLayout();
   1217                     }
   1218                 }
   1219             };
   1220 
   1221     private MediaPlayer.OnPreparedListener mPreparedListener =
   1222             new MediaPlayer.OnPreparedListener() {
   1223         @Override
   1224         public void onPrepared(MediaPlayer mp) {
   1225             if (DEBUG) {
   1226                 Log.d(TAG, "OnPreparedListener(). mCurrentState=" + mCurrentState
   1227                         + ", mTargetState=" + mTargetState);
   1228             }
   1229             mCurrentState = STATE_PREPARED;
   1230             // Create and set playback state for MediaControlView2
   1231             updatePlaybackState();
   1232 
   1233             if (mMediaSession != null) {
   1234                 extractTracks();
   1235                 extractMetadata();
   1236                 extractAudioMetadata();
   1237             }
   1238 
   1239             if (mMediaControlView != null) {
   1240                 mMediaControlView.setEnabled(true);
   1241             }
   1242             int videoWidth = mp.getVideoWidth();
   1243             int videoHeight = mp.getVideoHeight();
   1244 
   1245             // mSeekWhenPrepared may be changed after seekTo() call
   1246             long seekToPosition = mSeekWhenPrepared;
   1247             if (seekToPosition != 0) {
   1248                 mMediaController.getTransportControls().seekTo(seekToPosition);
   1249             }
   1250 
   1251             if (videoWidth != 0 && videoHeight != 0) {
   1252                 if (videoWidth != mVideoWidth || videoHeight != mVideoHeight) {
   1253                     mVideoWidth = videoWidth;
   1254                     mVideoHeight = videoHeight;
   1255                     mInstance.requestLayout();
   1256                 }
   1257 
   1258                 if (needToStart()) {
   1259                     mMediaController.getTransportControls().play();
   1260                 }
   1261             } else {
   1262                 // We don't know the video size yet, but should start anyway.
   1263                 // The video size might be reported to us later.
   1264                 if (needToStart()) {
   1265                     mMediaController.getTransportControls().play();
   1266                 }
   1267             }
   1268             // Get and set duration and title values as MediaMetadata for MediaControlView2
   1269             MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
   1270 
   1271             builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, mTitle);
   1272             builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, mMediaPlayer.getDuration());
   1273 
   1274             if (mMediaSession != null) {
   1275                 mMediaSession.setMetadata(builder.build());
   1276 
   1277                 if (mNeedUpdateMediaType) {
   1278                     mMediaSession.sendSessionEvent(
   1279                             MediaControlView2.EVENT_UPDATE_MEDIA_TYPE_STATUS, mMediaTypeData);
   1280                     mNeedUpdateMediaType = false;
   1281                 }
   1282             }
   1283         }
   1284     };
   1285 
   1286     private MediaPlayer.OnSeekCompleteListener mSeekCompleteListener =
   1287             new MediaPlayer.OnSeekCompleteListener() {
   1288                 @Override
   1289                 public void onSeekComplete(MediaPlayer mp) {
   1290                     updatePlaybackState();
   1291                 }
   1292     };
   1293 
   1294     private MediaPlayer.OnCompletionListener mCompletionListener =
   1295             new MediaPlayer.OnCompletionListener() {
   1296         @Override
   1297         @SuppressWarnings("deprecation")
   1298         public void onCompletion(MediaPlayer mp) {
   1299             mCurrentState = STATE_PLAYBACK_COMPLETED;
   1300             mTargetState = STATE_PLAYBACK_COMPLETED;
   1301             updatePlaybackState();
   1302             if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
   1303                 mAudioManager.abandonAudioFocus(null);
   1304             }
   1305         }
   1306     };
   1307 
   1308     private MediaPlayer.OnInfoListener mInfoListener = new MediaPlayer.OnInfoListener() {
   1309         @Override
   1310         public boolean onInfo(MediaPlayer mp, int what, int extra) {
   1311             if (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
   1312                 extractTracks();
   1313             }
   1314             return true;
   1315         }
   1316     };
   1317 
   1318     private MediaPlayer.OnErrorListener mErrorListener = new MediaPlayer.OnErrorListener() {
   1319         @Override
   1320         public boolean onError(MediaPlayer mp, int frameworkErr, int implErr) {
   1321             if (DEBUG) {
   1322                 Log.d(TAG, "Error: " + frameworkErr + "," + implErr);
   1323             }
   1324             mCurrentState = STATE_ERROR;
   1325             mTargetState = STATE_ERROR;
   1326             updatePlaybackState();
   1327 
   1328             if (mMediaControlView != null) {
   1329                 mMediaControlView.setVisibility(View.GONE);
   1330             }
   1331             return true;
   1332         }
   1333     };
   1334 
   1335     private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
   1336             new MediaPlayer.OnBufferingUpdateListener() {
   1337         @Override
   1338         public void onBufferingUpdate(MediaPlayer mp, int percent) {
   1339             mCurrentBufferPercentage = percent;
   1340             updatePlaybackState();
   1341         }
   1342     };
   1343 
   1344     private class MediaSessionCallback extends MediaSessionCompat.Callback {
   1345         @Override
   1346         public void onCommand(String command, Bundle args, ResultReceiver receiver) {
   1347             if (isRemotePlayback()) {
   1348                 mRoutePlayer.onCommand(command, args, receiver);
   1349             } else {
   1350                 switch (command) {
   1351                     case MediaControlView2.COMMAND_SHOW_SUBTITLE:
   1352                         doShowSubtitleCommand(args);
   1353                         break;
   1354                     case MediaControlView2.COMMAND_HIDE_SUBTITLE:
   1355                         doHideSubtitleCommand();
   1356                         break;
   1357                     case MediaControlView2.COMMAND_SELECT_AUDIO_TRACK:
   1358                         int audioIndex = args.getInt(MediaControlView2.KEY_SELECTED_AUDIO_INDEX,
   1359                                 INVALID_TRACK_INDEX);
   1360                         if (audioIndex != INVALID_TRACK_INDEX) {
   1361                             int audioTrackIndex = mAudioTrackIndices.get(audioIndex);
   1362                             if (audioTrackIndex != mSelectedAudioTrackIndex) {
   1363                                 mSelectedAudioTrackIndex = audioTrackIndex;
   1364                                 mMediaPlayer.selectTrack(mSelectedAudioTrackIndex);
   1365                             }
   1366                         }
   1367                         break;
   1368                     case MediaControlView2.COMMAND_SET_PLAYBACK_SPEED:
   1369                         float speed = args.getFloat(
   1370                                 MediaControlView2.KEY_PLAYBACK_SPEED, INVALID_SPEED);
   1371                         if (speed != INVALID_SPEED && speed != mSpeed) {
   1372                             setSpeed(speed);
   1373                             mSpeed = speed;
   1374                         }
   1375                         break;
   1376                     case MediaControlView2.COMMAND_MUTE:
   1377                         mVolumeLevel = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
   1378                         mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);
   1379                         break;
   1380                     case MediaControlView2.COMMAND_UNMUTE:
   1381                         mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, mVolumeLevel, 0);
   1382                         break;
   1383                 }
   1384             }
   1385             showController();
   1386         }
   1387 
   1388         @Override
   1389         public void onCustomAction(final String action, final Bundle extras) {
   1390             mCustomActionListenerRecord.first.execute(new Runnable() {
   1391                 @Override
   1392                 public void run() {
   1393                     mCustomActionListenerRecord.second.onCustomAction(action, extras);
   1394                 }
   1395             });
   1396             showController();
   1397         }
   1398 
   1399         @Override
   1400         public void onPlay() {
   1401             if (!isAudioGranted()) {
   1402                 requestAudioFocus(mAudioFocusType);
   1403             }
   1404 
   1405             if ((isInPlaybackState() && mCurrentView.hasAvailableSurface()) || isMusicMediaType()) {
   1406                 if (isRemotePlayback()) {
   1407                     mRoutePlayer.onPlay();
   1408                 } else {
   1409                     applySpeed();
   1410                     mMediaPlayer.start();
   1411                     mCurrentState = STATE_PLAYING;
   1412                     updatePlaybackState();
   1413                 }
   1414                 mCurrentState = STATE_PLAYING;
   1415             }
   1416             mTargetState = STATE_PLAYING;
   1417             if (DEBUG) {
   1418                 Log.d(TAG, "onPlay(). mCurrentState=" + mCurrentState
   1419                         + ", mTargetState=" + mTargetState);
   1420             }
   1421             showController();
   1422         }
   1423 
   1424         @Override
   1425         public void onPause() {
   1426             if (isInPlaybackState()) {
   1427                 if (isRemotePlayback()) {
   1428                     mRoutePlayer.onPlay();
   1429                     mCurrentState = STATE_PAUSED;
   1430                 } else if (mMediaPlayer.isPlaying()) {
   1431                     mMediaPlayer.pause();
   1432                     mCurrentState = STATE_PAUSED;
   1433                     updatePlaybackState();
   1434                 }
   1435             }
   1436             mTargetState = STATE_PAUSED;
   1437             if (DEBUG) {
   1438                 Log.d(TAG, "onPause(). mCurrentState=" + mCurrentState
   1439                         + ", mTargetState=" + mTargetState);
   1440             }
   1441             showController();
   1442         }
   1443 
   1444         @Override
   1445         public void onSeekTo(long pos) {
   1446             if (isInPlaybackState()) {
   1447                 if (isRemotePlayback()) {
   1448                     mRoutePlayer.onPlay();
   1449                 } else {
   1450                     if (android.os.Build.VERSION.SDK_INT < 26) {
   1451                         mMediaPlayer.seekTo((int) pos);
   1452                     } else {
   1453                         mMediaPlayer.seekTo(pos, MediaPlayer.SEEK_PREVIOUS_SYNC);
   1454                     }
   1455                     mSeekWhenPrepared = 0;
   1456                 }
   1457             } else {
   1458                 mSeekWhenPrepared = pos;
   1459             }
   1460             showController();
   1461         }
   1462 
   1463         @Override
   1464         public void onStop() {
   1465             if (isRemotePlayback()) {
   1466                 mRoutePlayer.onPlay();
   1467             } else {
   1468                 resetPlayer();
   1469             }
   1470             showController();
   1471         }
   1472     }
   1473 }
   1474