MediaControlView2: Add Music UX
1. Create Music View inside VideoView2
- Dynamically change layouts based on size and orientation
- Disable touch when Full size mode
2. Create Music Mode button settings inside MediaControlView2
Bug: 73873457
Test: Manually run VideoViewTest.apk
Change-Id: Iba47ab40b7eb24147c09efbe997bd8e765719d69
diff --git a/packages/MediaComponents/res/drawable/ic_default_album_image.xml b/packages/MediaComponents/res/drawable/ic_default_album_image.xml
new file mode 100644
index 0000000..1cee643
--- /dev/null
+++ b/packages/MediaComponents/res/drawable/ic_default_album_image.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="512dp"
+ android:height="512dp"
+ android:viewportWidth="512"
+ android:viewportHeight="512">
+
+ <path
+ android:fillColor="#616161"
+ android:pathData="M 0 0 H 512 V 512 H 0 V 0 Z" />
+ <path
+ android:fillColor="#525252"
+ android:pathData="M256,151v123.14c-6.88-4.02-14.82-6.48-23.33-6.48 c-25.78,0-46.67,20.88-46.67,46.67c0,25.78,20.88,46.67,46.67,46.67s46.67-20.88,46.67-46.67V197.67H326V151H256z" />
+</vector>
diff --git a/packages/MediaComponents/res/layout/embedded_music.xml b/packages/MediaComponents/res/layout/embedded_music.xml
new file mode 100644
index 0000000..3e4d365
--- /dev/null
+++ b/packages/MediaComponents/res/layout/embedded_music.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="0.25"/>
+
+ <ImageView
+ android:id="@+id/album"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="0.5"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_default_album_image" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="0.25"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/full_landscape_music.xml b/packages/MediaComponents/res/layout/full_landscape_music.xml
new file mode 100644
index 0000000..8ce7058
--- /dev/null
+++ b/packages/MediaComponents/res/layout/full_landscape_music.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#B300FF00"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/music_image"
+ style="@style/FullMusicLandscape.Image">
+
+ <ImageView
+ android:id="@+id/album"
+ android:layout_width="@dimen/mcv2_full_album_image_landscape_size"
+ android:layout_height="@dimen/mcv2_full_album_image_landscape_size"
+ android:src="@drawable/ic_default_album_image"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/music_text"
+ style="@style/FullMusicLandscape.Text">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/mcv2_music_title_unknown_text"
+ android:textSize="20sp"
+ android:textStyle="bold"
+ android:textColor="#FFFFFF" />
+ <TextView
+ android:id="@+id/artist"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/mcv2_music_artist_unknown_text"
+ android:textSize="16sp"
+ android:textColor="#BBBBBB" />
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/full_portrait_music.xml b/packages/MediaComponents/res/layout/full_portrait_music.xml
new file mode 100644
index 0000000..75f1bb3
--- /dev/null
+++ b/packages/MediaComponents/res/layout/full_portrait_music.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/music_image"
+ style="@style/FullMusicPortrait.Image">
+
+ <ImageView
+ android:id="@+id/album"
+ android:layout_width="@dimen/mcv2_full_album_image_portrait_size"
+ android:layout_height="@dimen/mcv2_full_album_image_portrait_size"
+ android:src="@drawable/ic_default_album_image"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/music_text"
+ style="@style/FullMusicPortrait.Text">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="@dimen/mcv2_full_album_image_portrait_size"
+ android:layout_height="wrap_content"
+ android:text="@string/mcv2_music_title_unknown_text"
+ android:textSize="20sp"
+ android:textStyle="bold"
+ android:textColor="#FFFFFF" />
+ <TextView
+ android:id="@+id/artist"
+ android:layout_width="@dimen/mcv2_full_album_image_portrait_size"
+ android:layout_height="wrap_content"
+ android:text="@string/mcv2_music_artist_unknown_text"
+ android:textSize="16sp"
+ android:textColor="#BBBBBB" />
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/media_controller.xml b/packages/MediaComponents/res/layout/media_controller.xml
index dfda840..ffaf03f 100644
--- a/packages/MediaComponents/res/layout/media_controller.xml
+++ b/packages/MediaComponents/res/layout/media_controller.xml
@@ -23,8 +23,6 @@
<RelativeLayout
android:id="@+id/title_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
style="@style/TitleBar">
<LinearLayout
diff --git a/packages/MediaComponents/res/values/dimens.xml b/packages/MediaComponents/res/values/dimens.xml
index 8d72d40..62bc196 100644
--- a/packages/MediaComponents/res/values/dimens.xml
+++ b/packages/MediaComponents/res/values/dimens.xml
@@ -62,5 +62,8 @@
<dimen name="mcv2_minimal_icon_size">24dp</dimen>
<dimen name="mcv2_icon_margin">10dp</dimen>
+ <dimen name="mcv2_full_album_image_portrait_size">232dp</dimen>
+ <dimen name="mcv2_full_album_image_landscape_size">176dp</dimen>
+
<!-- TODO: adjust bottom bar view -->
</resources>
diff --git a/packages/MediaComponents/res/values/strings.xml b/packages/MediaComponents/res/values/strings.xml
index aaceac8..2597a3b 100644
--- a/packages/MediaComponents/res/values/strings.xml
+++ b/packages/MediaComponents/res/values/strings.xml
@@ -129,6 +129,10 @@
<string name="MediaControlView2_audio_track_number_text">
Track <xliff:g id="audio_number" example="1">%1$s</xliff:g>
</string>
+ <!-- Text for displaying unknown song title. -->
+ <string name="mcv2_music_title_unknown_text">Song title unknown</string>
+ <!-- Text for displaying unknown artist name. -->
+ <string name="mcv2_music_artist_unknown_text">Artist unknown</string>
<!--Content Descriptions -->
<string name="mcv2_back_button_desc">Back</string>
diff --git a/packages/MediaComponents/res/values/style.xml b/packages/MediaComponents/res/values/style.xml
index 0be04e6..76a21c4 100644
--- a/packages/MediaComponents/res/values/style.xml
+++ b/packages/MediaComponents/res/values/style.xml
@@ -95,7 +95,10 @@
</style>
<style name="TitleBar">
+ <item name="android:layout_width">match_parent</item>
<item name="android:layout_height">46dp</item>
+ <item name="android:layout_marginLeft">5dp</item>
+ <item name="android:layout_marginRight">5dp</item>
</style>
<style name="TitleBarButton">
@@ -182,4 +185,39 @@
<item name="android:src">@drawable/ic_high_quality</item>
<item name="android:contentDescription">@string/mcv2_video_quality_button_desc</item>
</style>
+
+ <style name="FullMusicPortrait">
+ <item name="android:layout_height">0dp</item>
+ </style>
+
+ <style name="FullMusicPortrait.Image">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_weight">0.6</item>
+ <item name="android:gravity">center</item>
+ </style>
+
+ <style name="FullMusicPortrait.Text">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_weight">0.4</item>
+ <item name="android:gravity">top|center</item>
+ <item name="android:orientation">vertical</item>
+ </style>
+
+ <style name="FullMusicLandscape">
+ <item name="android:layout_width">0dp</item>
+ </style>
+
+ <style name="FullMusicLandscape.Image">
+ <item name="android:layout_height">match_parent</item>
+ <item name="android:layout_weight">0.35</item>
+ <item name="android:gravity">center|right</item>
+ </style>
+
+ <style name="FullMusicLandscape.Text">
+ <item name="android:layout_height">match_parent</item>
+ <item name="android:layout_weight">0.65</item>
+ <item name="android:layout_marginLeft">24dp</item>
+ <item name="android:gravity">center|left</item>
+ <item name="android:orientation">vertical</item>
+ </style>
</resources>
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
index 4cdc41d..7134004 100644
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
@@ -16,7 +16,9 @@
package com.android.widget;
+import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Point;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.PlaybackState;
@@ -31,6 +33,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
@@ -102,6 +105,10 @@
private static final int SETTINGS_MODE_MAIN = 5;
private static final int PLAYBACK_SPEED_1x_INDEX = 3;
+ private static final int MEDIA_TYPE_DEFAULT = 0;
+ private static final int MEDIA_TYPE_MUSIC = 1;
+ private static final int MEDIA_TYPE_ADVERTISEMENT = 2;
+
private static final int SIZE_TYPE_EMBEDDED = 0;
private static final int SIZE_TYPE_FULL = 1;
// TODO: add support for Minimal size type.
@@ -123,6 +130,7 @@
private int mDuration;
private int mPrevState;
private int mPrevWidth;
+ private int mPrevHeight;
private int mOriginalLeftBarWidth;
private int mVideoTrackCount;
private int mAudioTrackCount;
@@ -137,8 +145,8 @@
private int mEmbeddedSettingsItemHeight;
private int mFullSettingsItemHeight;
private int mSettingsWindowMargin;
+ private int mMediaType;
private int mSizeType;
- private int mOrientation;
private long mPlaybackActions;
private boolean mDragging;
private boolean mIsFullScreen;
@@ -148,6 +156,7 @@
private boolean mSeekAvailable;
private boolean mIsAdvertisement;
private boolean mIsMute;
+ private boolean mNeedUXUpdate;
// Relating to Title Bar View
private View mRoot;
@@ -334,28 +343,80 @@
public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure_impl(widthMeasureSpec, heightMeasureSpec);
- if (mPrevWidth != mInstance.getMeasuredWidth()) {
- int currWidth = mInstance.getMeasuredWidth();
+ // Update layout when this view's width changes in order to avoid any UI overlap between
+ // transport controls.
+ if (mPrevWidth != mInstance.getMeasuredWidth()
+ || mPrevHeight != mInstance.getMeasuredHeight() || mNeedUXUpdate) {
+ // Dismiss SettingsWindow if it is showing.
+ mSettingsWindow.dismiss();
- int iconSize = mResources.getDimensionPixelSize(R.dimen.mcv2_full_icon_size);
- int marginSize = mResources.getDimensionPixelSize(R.dimen.mcv2_icon_margin);
- int bottomBarRightWidthMax = iconSize * 4 + marginSize * 8;
-
- int fullWidth = mTransportControls.getWidth() + mTimeView.getWidth()
- + bottomBarRightWidthMax;
// These views may not have been initialized yet.
if (mTransportControls.getWidth() == 0 || mTimeView.getWidth() == 0) {
return;
}
- if (mSizeType == SIZE_TYPE_EMBEDDED && fullWidth <= currWidth) {
- updateLayoutForSizeChange(SIZE_TYPE_FULL);
- } else if (mSizeType == SIZE_TYPE_FULL && fullWidth > currWidth) {
- updateLayoutForSizeChange(SIZE_TYPE_EMBEDDED);
+
+ int currWidth = mInstance.getMeasuredWidth();
+ int currHeight = mInstance.getMeasuredHeight();
+ WindowManager manager = (WindowManager) mInstance.getContext().getApplicationContext()
+ .getSystemService(Context.WINDOW_SERVICE);
+ Point screenSize = new Point();
+ manager.getDefaultDisplay().getSize(screenSize);
+ int screenWidth = screenSize.x;
+ int screenHeight = screenSize.y;
+ int screenMaxLength = Math.max(screenWidth, screenHeight);
+
+ // TODO: add support for Advertisement Mode.
+ if (mMediaType == MEDIA_TYPE_DEFAULT) {
+ int iconSize = mResources.getDimensionPixelSize(R.dimen.mcv2_full_icon_size);
+ int marginSize = mResources.getDimensionPixelSize(R.dimen.mcv2_icon_margin);
+ // Currently, the maximum number of icons the BottomBar can have is 4 icons. When
+ // calculating the minimum amount of space needed to place all these icons, however,
+ // we also need to add the amount of margins on both the right and left sides of the
+ // buttons.
+ int bottomBarRightWidthMax = 4 * (iconSize + marginSize * 2);
+ int fullWidth = mTransportControls.getWidth() + mTimeView.getWidth()
+ + bottomBarRightWidthMax;
+ if (fullWidth > screenMaxLength) {
+ // TODO: screen may be smaller than the length needed for Full size.
+ }
+ if (currWidth == screenMaxLength) {
+ if (mSizeType != SIZE_TYPE_FULL) {
+ updateDefaultLayoutForSizeChange(SIZE_TYPE_FULL);
+ }
+ } else {
+ if (mSizeType != SIZE_TYPE_EMBEDDED) {
+ updateDefaultLayoutForSizeChange(SIZE_TYPE_EMBEDDED);
+ }
+ }
+ } else if (mMediaType == MEDIA_TYPE_MUSIC) {
+ if (mNeedUXUpdate) {
+ // One-time operation for Music media type
+ mBasicControls.removeView(mMuteButton);
+ mExtraControls.addView(mMuteButton, 0);
+ mVideoQualityButton.setVisibility(View.GONE);
+ mFfwdButton.setVisibility(View.GONE);
+ mRewButton.setVisibility(View.GONE);
+ }
+ mNeedUXUpdate = false;
+
+ if (currWidth == screenWidth && currHeight == screenHeight) {
+ if (mSizeType != SIZE_TYPE_FULL) {
+ updateDefaultLayoutForSizeChange(SIZE_TYPE_FULL);
+ mTitleView.setVisibility(View.GONE);
+ }
+ } else {
+ if (mSizeType != SIZE_TYPE_EMBEDDED) {
+ updateDefaultLayoutForSizeChange(SIZE_TYPE_EMBEDDED);
+ mTitleView.setVisibility(View.VISIBLE);
+ }
+ }
}
- // Dismiss SettingsWindow if it is showing.
- mSettingsWindow.dismiss();
mPrevWidth = currWidth;
+ mPrevHeight = currHeight;
}
+ // TODO: move this to a different location.
+ // Update title bar parameters in order to avoid overlap between title view and the right
+ // side of the title bar.
updateTitleBarLayout();
}
@@ -482,6 +543,7 @@
mBackButton = v.findViewById(R.id.back);
if (mBackButton != null) {
mBackButton.setOnClickListener(mBackListener);
+ mBackButton.setVisibility(View.GONE);
}
mRouteButton = v.findViewById(R.id.cast);
@@ -862,7 +924,7 @@
}
Bundle args = new Bundle();
args.putBoolean(ARGUMENT_KEY_FULLSCREEN, isEnteringFullScreen);
- mController.sendCommand(MediaControlView2Impl.COMMAND_SET_FULLSCREEN, args, null);
+ mController.sendCommand(COMMAND_SET_FULLSCREEN, args, null);
mIsFullScreen = isEnteringFullScreen;
}
@@ -1040,6 +1102,34 @@
}
}
+ private void updateAudioMetadata() {
+ if (mMediaType != MEDIA_TYPE_MUSIC) {
+ return;
+ }
+
+ if (mMetadata != null) {
+ String titleText = "";
+ String artistText = "";
+ if (mMetadata.containsKey(MediaMetadata.METADATA_KEY_TITLE)) {
+ titleText = mMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
+ } else {
+ titleText = mResources.getString(R.string.mcv2_music_title_unknown_text);
+ }
+
+ if (mMetadata.containsKey(MediaMetadata.METADATA_KEY_ARTIST)) {
+ artistText = mMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
+ } else {
+ artistText = mResources.getString(R.string.mcv2_music_artist_unknown_text);
+ }
+
+ // Update title for Embedded size type
+ mTitleView.setText(titleText + " - " + artistText);
+
+ // Set to true to update layout inside onMeasure()
+ mNeedUXUpdate = true;
+ }
+ }
+
private void updateLayout() {
if (mIsAdvertisement) {
mRewButton.setVisibility(View.GONE);
@@ -1071,7 +1161,7 @@
}
}
- private void updateLayoutForSizeChange(int sizeType) {
+ private void updateDefaultLayoutForSizeChange(int sizeType) {
mSizeType = sizeType;
RelativeLayout.LayoutParams params =
(RelativeLayout.LayoutParams) mTimeView.getLayoutParams();
@@ -1080,12 +1170,14 @@
mBottomBarLeftView.removeView(mTransportControls);
mBottomBarLeftView.setVisibility(View.GONE);
mTransportControls = inflateTransportControls(R.layout.embedded_transport_controls);
- mCenterView.addView(mTransportControls, 0);
+ mCenterView.addView(mTransportControls);
if (params.getRule(RelativeLayout.LEFT_OF) != 0) {
params.removeRule(RelativeLayout.LEFT_OF);
params.addRule(RelativeLayout.RIGHT_OF, R.id.bottom_bar_left);
}
+
+ mBackButton.setVisibility(View.GONE);
break;
case SIZE_TYPE_FULL:
mCenterView.removeView(mTransportControls);
@@ -1097,6 +1189,8 @@
params.removeRule(RelativeLayout.RIGHT_OF);
params.addRule(RelativeLayout.LEFT_OF, R.id.bottom_bar_right);
}
+
+ mBackButton.setVisibility(View.VISIBLE);
break;
case SIZE_TYPE_MINIMAL:
// TODO: implement
@@ -1143,6 +1237,11 @@
mPrevButton.setOnClickListener(mPrevListener);
mPrevButton.setVisibility(View.GONE);
}
+
+ if (mMediaType == MEDIA_TYPE_MUSIC) {
+ mFfwdButton.setVisibility(View.GONE);
+ mRewButton.setVisibility(View.GONE);
+ }
return v;
}
@@ -1250,10 +1349,12 @@
if ((newActions & PlaybackState.ACTION_PAUSE) != 0) {
mPlayPauseButton.setVisibility(View.VISIBLE);
}
- if ((newActions & PlaybackState.ACTION_REWIND) != 0) {
+ if ((newActions & PlaybackState.ACTION_REWIND) != 0
+ && mMediaType != MEDIA_TYPE_MUSIC) {
mRewButton.setVisibility(View.VISIBLE);
}
- if ((newActions & PlaybackState.ACTION_FAST_FORWARD) != 0) {
+ if ((newActions & PlaybackState.ACTION_FAST_FORWARD) != 0
+ && mMediaType != MEDIA_TYPE_MUSIC) {
mFfwdButton.setVisibility(View.VISIBLE);
}
if ((newActions & PlaybackState.ACTION_SEEK_TO) != 0) {
@@ -1295,6 +1396,7 @@
mMetadata = metadata;
updateDuration();
updateTitle();
+ updateAudioMetadata();
}
@Override
@@ -1321,6 +1423,9 @@
mAudioTrackList.add(mResources.getString(
R.string.MediaControlView2_audio_track_none_text));
}
+ if (mVideoTrackCount == 0 && mAudioTrackCount > 0) {
+ mMediaType = MEDIA_TYPE_MUSIC;
+ }
mSubtitleTrackCount = extras.getInt(KEY_SUBTITLE_TRACK_COUNT);
mSubtitleDescriptionsList = new ArrayList<String>();
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index 46ae359..156ee5a 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -17,6 +17,13 @@
package com.android.widget;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
@@ -29,6 +36,7 @@
import android.media.SubtitleData;
import android.media.MediaItem2;
import android.media.MediaMetadata2;
+import android.media.MediaMetadataRetriever;
import android.media.Metadata;
import android.media.PlaybackParams;
import android.media.TimedText;
@@ -45,19 +53,26 @@
import android.os.ResultReceiver;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import android.widget.ImageView;
import android.widget.MediaControlView2;
+import android.widget.TextView;
import android.widget.VideoView2;
+import com.android.internal.graphics.palette.Palette;
import com.android.media.RoutePlayer;
import com.android.media.subtitle.ClosedCaptionRenderer;
import com.android.media.subtitle.SubtitleController;
import com.android.media.subtitle.SubtitleTrack;
+import com.android.media.update.ApiHelper;
+import com.android.media.update.R;
import com.android.support.mediarouter.media.MediaItemStatus;
import com.android.support.mediarouter.media.MediaControlIntent;
import com.android.support.mediarouter.media.MediaRouter;
@@ -87,6 +102,11 @@
private static final int INVALID_TRACK_INDEX = -1;
private static final float INVALID_SPEED = 0f;
+ private static final int SIZE_TYPE_EMBEDDED = 0;
+ private static final int SIZE_TYPE_FULL = 1;
+ // TODO: add support for Minimal size type.
+ private static final int SIZE_TYPE_MINIMAL = 2;
+
private AccessibilityManager mAccessibilityManager;
private AudioManager mAudioManager;
private AudioAttributes mAudioAttributes;
@@ -107,10 +127,24 @@
private MediaController mMediaController;
private Metadata mMetadata;
private MediaMetadata2 mMediaMetadata;
+ private MediaMetadataRetriever mRetriever;
private boolean mNeedUpdateMediaType;
private Bundle mMediaTypeData;
private String mTitle;
+ // TODO: move music view inside SurfaceView/TextureView or implement VideoViewInterface.
+ private WindowManager mManager;
+ private Resources mResources;
+ private View mMusicView;
+ private Drawable mMusicAlbumDrawable;
+ private String mMusicTitleText;
+ private String mMusicArtistText;
+ private boolean mIsMusicMediaType;
+ private int mPrevWidth;
+ private int mPrevHeight;
+ private int mDominantColor;
+ private int mSizeType;
+
private PlaybackState.Builder mStateBuilder;
private List<PlaybackState.CustomAction> mCustomActionList;
private int mTargetState = STATE_IDLE;
@@ -259,10 +293,8 @@
mInstance.addView(mSurfaceView);
mCurrentView = mSurfaceView;
- LayoutParams subtitleParams = new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT);
mSubtitleView = new SubtitleView(mInstance.getContext());
- mSubtitleView.setLayoutParams(subtitleParams);
+ mSubtitleView.setLayoutParams(params);
mSubtitleView.setBackgroundColor(0);
mInstance.addView(mSubtitleView);
@@ -492,6 +524,14 @@
// TODO: remove this after moving MediaSession creating code inside initializing VideoView2
if (mCurrentState == STATE_PREPARED) {
extractTracks();
+ extractMetadata();
+ extractAudioMetadata();
+ if (mNeedUpdateMediaType) {
+ mMediaSession.sendSessionEvent(
+ MediaControlView2Impl.EVENT_UPDATE_MEDIA_TYPE_STATUS,
+ mMediaTypeData);
+ mNeedUpdateMediaType = false;
+ }
}
}
@@ -516,7 +556,9 @@
+ ", mTargetState=" + mTargetState);
}
if (ev.getAction() == MotionEvent.ACTION_UP && mMediaControlView != null) {
- toggleMediaControlViewVisibility();
+ if (!mIsMusicMediaType || mSizeType != SIZE_TYPE_FULL) {
+ toggleMediaControlViewVisibility();
+ }
}
return super.onTouchEvent_impl(ev);
@@ -525,7 +567,9 @@
@Override
public boolean onTrackballEvent_impl(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_UP && mMediaControlView != null) {
- toggleMediaControlViewVisibility();
+ if (!mIsMusicMediaType || mSizeType != SIZE_TYPE_FULL) {
+ toggleMediaControlViewVisibility();
+ }
}
return super.onTrackballEvent_impl(ev);
@@ -537,6 +581,48 @@
return super.dispatchTouchEvent_impl(ev);
}
+ @Override
+ public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure_impl(widthMeasureSpec, heightMeasureSpec);
+
+ if (mIsMusicMediaType) {
+ if (mPrevWidth != mInstance.getMeasuredWidth()
+ || mPrevHeight != mInstance.getMeasuredHeight()) {
+ int currWidth = mInstance.getMeasuredWidth();
+ int currHeight = mInstance.getMeasuredHeight();
+ Point screenSize = new Point();
+ mManager.getDefaultDisplay().getSize(screenSize);
+ int screenWidth = screenSize.x;
+ int screenHeight = screenSize.y;
+
+ if (currWidth == screenWidth && currHeight == screenHeight) {
+ int orientation = retrieveOrientation();
+ if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
+ inflateMusicView(R.layout.full_landscape_music);
+ } else {
+ inflateMusicView(R.layout.full_portrait_music);
+ }
+
+ if (mSizeType != SIZE_TYPE_FULL) {
+ mSizeType = SIZE_TYPE_FULL;
+ // Remove existing mFadeOut callback
+ mMediaControlView.removeCallbacks(mFadeOut);
+ mMediaControlView.setVisibility(View.VISIBLE);
+ }
+ } else {
+ if (mSizeType != SIZE_TYPE_EMBEDDED) {
+ mSizeType = SIZE_TYPE_EMBEDDED;
+ inflateMusicView(R.layout.embedded_music);
+ // Add new mFadeOut callback
+ mMediaControlView.postDelayed(mFadeOut, mShowControllerIntervalMs);
+ }
+ }
+ mPrevWidth = currWidth;
+ mPrevHeight = currHeight;
+ }
+ }
+ }
+
///////////////////////////////////////////////////
// Implements VideoViewInterface.SurfaceListener
///////////////////////////////////////////////////
@@ -663,6 +749,8 @@
if (scheme != null && scheme.equals("file")) {
mTitle = uri.getLastPathSegment();
}
+ mRetriever = new MediaMetadataRetriever();
+ mRetriever.setDataSource(mInstance.getContext(), uri);
if (DEBUG) {
Log.d(TAG, "openVideo(). mCurrentState=" + mCurrentState
@@ -798,7 +886,8 @@
private void showController() {
// TODO: Decide what to show when the state is not in playback state
- if (mMediaControlView == null || !isInPlaybackState()) {
+ if (mMediaControlView == null || !isInPlaybackState()
+ || (mIsMusicMediaType && mSizeType == SIZE_TYPE_FULL)) {
return;
}
mMediaControlView.removeCallbacks(mFadeOut);
@@ -898,6 +987,9 @@
if (mAudioTrackIndices.size() > 0) {
mSelectedAudioTrackIndex = 0;
}
+ if (mVideoTrackIndices.size() == 0 && mAudioTrackIndices.size() > 0) {
+ mIsMusicMediaType = true;
+ }
Bundle data = new Bundle();
data.putInt(MediaControlView2Impl.KEY_VIDEO_TRACK_COUNT, mVideoTrackIndices.size());
@@ -909,6 +1001,110 @@
mMediaSession.sendSessionEvent(MediaControlView2Impl.EVENT_UPDATE_TRACK_STATUS, data);
}
+ private void extractMetadata() {
+ // Get and set duration and title values as MediaMetadata for MediaControlView2
+ MediaMetadata.Builder builder = new MediaMetadata.Builder();
+ if (mMetadata != null && mMetadata.has(Metadata.TITLE)) {
+ mTitle = mMetadata.getString(Metadata.TITLE);
+ }
+ builder.putString(MediaMetadata.METADATA_KEY_TITLE, mTitle);
+ builder.putLong(
+ MediaMetadata.METADATA_KEY_DURATION, mMediaPlayer.getDuration());
+
+ if (mMediaSession != null) {
+ mMediaSession.setMetadata(builder.build());
+ }
+ }
+
+ private void extractAudioMetadata() {
+ if (!mIsMusicMediaType) {
+ return;
+ }
+
+ mResources = ApiHelper.getLibResources(mInstance.getContext());
+ mManager = (WindowManager) mInstance.getContext().getApplicationContext()
+ .getSystemService(Context.WINDOW_SERVICE);
+
+ byte[] album = mRetriever.getEmbeddedPicture();
+ if (album != null) {
+ Bitmap bitmap = BitmapFactory.decodeByteArray(album, 0, album.length);
+ mMusicAlbumDrawable = new BitmapDrawable(bitmap);
+
+ // TODO: replace with visualizer
+ Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
+ public void onGenerated(Palette palette) {
+ // TODO: add dominant color for default album image.
+ mDominantColor = palette.getDominantColor(0);
+ if (mMusicView != null) {
+ mMusicView.setBackgroundColor(mDominantColor);
+ }
+ }
+ });
+ } else {
+ mMusicAlbumDrawable = mResources.getDrawable(R.drawable.ic_default_album_image);
+ }
+
+ String title = mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
+ if (title != null) {
+ mMusicTitleText = title;
+ } else {
+ mMusicTitleText = mResources.getString(R.string.mcv2_music_title_unknown_text);
+ }
+
+ String artist = mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
+ if (artist != null) {
+ mMusicArtistText = artist;
+ } else {
+ mMusicArtistText = mResources.getString(R.string.mcv2_music_artist_unknown_text);
+ }
+
+ // Send title and artist string to MediaControlView2
+ MediaMetadata.Builder builder = new MediaMetadata.Builder();
+ builder.putString(MediaMetadata.METADATA_KEY_TITLE, mMusicTitleText);
+ builder.putString(MediaMetadata.METADATA_KEY_ARTIST, mMusicArtistText);
+ mMediaSession.setMetadata(builder.build());
+
+ // Display Embedded mode as default
+ mInstance.removeView(mSurfaceView);
+ mInstance.removeView(mTextureView);
+ inflateMusicView(R.layout.embedded_music);
+ }
+
+ private int retrieveOrientation() {
+ DisplayMetrics dm = Resources.getSystem().getDisplayMetrics();
+ int width = dm.widthPixels;
+ int height = dm.heightPixels;
+
+ return (height > width) ?
+ ActivityInfo.SCREEN_ORIENTATION_PORTRAIT :
+ ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+ }
+
+ private void inflateMusicView(int layoutId) {
+ mInstance.removeView(mMusicView);
+
+ View v = ApiHelper.inflateLibLayout(mInstance.getContext(), layoutId);
+ v.setBackgroundColor(mDominantColor);
+
+ ImageView albumView = v.findViewById(R.id.album);
+ if (albumView != null) {
+ albumView.setImageDrawable(mMusicAlbumDrawable);
+ }
+
+ TextView titleView = v.findViewById(R.id.title);
+ if (titleView != null) {
+ titleView.setText(mMusicTitleText);
+ }
+
+ TextView artistView = v.findViewById(R.id.artist);
+ if (artistView != null) {
+ artistView.setText(mMusicArtistText);
+ }
+
+ mMusicView = v;
+ mInstance.addView(mMusicView, 0);
+ }
+
OnSubtitleDataListener mSubtitleListener =
new OnSubtitleDataListener() {
@Override
@@ -1007,6 +1203,8 @@
// TODO: create MediaSession when initializing VideoView2
if (mMediaSession != null) {
extractTracks();
+ extractMetadata();
+ extractAudioMetadata();
}
if (mMediaControlView != null) {
@@ -1046,27 +1244,6 @@
mMediaController.getTransportControls().play();
}
}
- // Get and set duration and title values as MediaMetadata for MediaControlView2
- MediaMetadata.Builder builder = new MediaMetadata.Builder();
- if (mMetadata != null && mMetadata.has(Metadata.TITLE)) {
- mTitle = mMetadata.getString(Metadata.TITLE);
- }
- builder.putString(MediaMetadata.METADATA_KEY_TITLE, mTitle);
- builder.putLong(
- MediaMetadata.METADATA_KEY_DURATION, mMediaPlayer.getDuration());
-
- if (mMediaSession != null) {
- mMediaSession.setMetadata(builder.build());
-
- // TODO: merge this code with the above code when integrating with
- // MediaSession2.
- if (mNeedUpdateMediaType) {
- mMediaSession.sendSessionEvent(
- MediaControlView2Impl.EVENT_UPDATE_MEDIA_TYPE_STATUS,
- mMediaTypeData);
- mNeedUpdateMediaType = false;
- }
- }
}
private void onCompletion(MediaPlayer2 mp, DataSourceDesc dsd) {
@@ -1153,7 +1330,7 @@
@Override
public void onPlay() {
- if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) {
+ if (isInPlaybackState() && (mCurrentView.hasAvailableSurface() || mIsMusicMediaType)) {
if (isRemotePlayback()) {
mRoutePlayer.onPlay();
} else {