summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2018-05-20 07:20:27 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2018-05-20 07:20:27 +0000
commit8c87b5efeb47ccad6e04450bc46c7c1a4894091f (patch)
tree978f40544280f2e2be23f64ded330a4f7f259d79
parentd66d86d340cf948acbb34cfa7c68afcf4cdd7714 (diff)
parent1d3e7e02676ed495b9f99701fa4647aa524d3f1b (diff)
downloadMedia-8c87b5efeb47ccad6e04450bc46c7c1a4894091f.tar.gz
Snap for 4793185 from 1d3e7e02676ed495b9f99701fa4647aa524d3f1b to pi-release
Change-Id: I1876cfe2965675b4b40817ab9115b7f12dce8071
-rw-r--r--AndroidManifest.xml1
-rw-r--r--res/layout-h1200dp/fragment_metadata.xml65
-rw-r--r--res/layout-h1200dp/fragment_metadata_with_queue.xml (renamed from res/layout/fragment_metadata_with_queue.xml)1
-rw-r--r--res/layout-h1200dp/fragment_playback_with_queue.xml (renamed from res/layout/fragment_playback_with_queue.xml)0
-rw-r--r--res/layout/browse_state.xml51
-rw-r--r--res/layout/fragment_browse.xml28
-rw-r--r--res/layout/fragment_empty.xml10
-rw-r--r--res/layout/fragment_metadata.xml34
-rw-r--r--res/layout/media_activity.xml6
-rw-r--r--res/values-h1200dp/bools.xml2
-rw-r--r--res/values-h1200dp/dimens.xml (renamed from res/values-h600dp/dimens.xml)5
-rw-r--r--res/values/bools.xml4
-rw-r--r--res/values/dimens.xml3
-rw-r--r--res/values/integers.xml2
-rw-r--r--src/com/android/car/media/BrowseFragment.java14
-rw-r--r--src/com/android/car/media/EmptyFragment.java72
-rw-r--r--src/com/android/car/media/MediaActivity.java180
-rw-r--r--src/com/android/car/media/MetadataController.java7
-rw-r--r--src/com/android/car/media/PlaybackFragment.java42
-rw-r--r--src/com/android/car/media/browse/BrowseAdapter.java92
-rw-r--r--src/com/android/car/media/drawer/MediaItemsFetcher.java38
-rw-r--r--src/com/android/car/media/widgets/AppBarView.java19
-rw-r--r--src/com/android/car/media/widgets/ViewUtils.java20
23 files changed, 473 insertions, 223 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 47da7be..76e2464 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,6 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ android:sharedUserId="com.android.car.media"
package="com.android.car.media" >
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
diff --git a/res/layout-h1200dp/fragment_metadata.xml b/res/layout-h1200dp/fragment_metadata.xml
new file mode 100644
index 0000000..63412aa
--- /dev/null
+++ b/res/layout-h1200dp/fragment_metadata.xml
@@ -0,0 +1,65 @@
+<?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.
+-->
+<merge
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:showIn="@layout/fragment_playback">
+
+ <ImageView
+ android:id="@+id/album_art"
+ android:layout_width="@dimen/playback_album_art_size_large"
+ android:layout_height="@dimen/playback_album_art_size_large"
+ android:layout_marginEnd="@dimen/car_keyline_2"
+ android:layout_marginStart="@dimen/car_keyline_2"
+ android:contentDescription="@string/album_art"
+ android:background="@color/car_body1_light"
+ android:scaleType="centerCrop"
+ android:transitionName="@string/album_art"
+ app:layout_constraintBottom_toTopOf="@id/metadata_subcontainer"
+ app:layout_constraintEnd_toEndOf="@+id/margin_end"
+ app:layout_constraintStart_toStartOf="@+id/margin_start"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_chainStyle="packed"
+ tools:src="@drawable/ic_person"/>
+
+ <include
+ android:id="@+id/metadata_subcontainer"
+ layout="@layout/metadata_normal"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/car_padding_5"
+ android:layout_marginBottom="@dimen/playback_controls_margin"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="@+id/album_art"
+ app:layout_constraintStart_toStartOf="@+id/album_art"
+ app:layout_constraintTop_toBottomOf="@+id/album_art"/>
+
+ <androidx.car.widget.PagedListView
+ android:id="@+id/queue_list"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginTop="@dimen/playback_album_art_size_normal"
+ android:layout_marginBottom="@dimen/playback_controls_margin"
+ android:visibility="gone"
+ app:dividerEndMargin="@dimen/car_keyline_1"
+ app:dividerStartMargin="@dimen/car_keyline_1"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/margin_top"
+ app:listDividerColor="@color/car_list_divider_light"/>
+</merge>
diff --git a/res/layout/fragment_metadata_with_queue.xml b/res/layout-h1200dp/fragment_metadata_with_queue.xml
index f3474f0..26bfb26 100644
--- a/res/layout/fragment_metadata_with_queue.xml
+++ b/res/layout-h1200dp/fragment_metadata_with_queue.xml
@@ -28,6 +28,7 @@
android:layout_marginTop="@dimen/car_padding_4"
android:layout_marginStart="@dimen/car_keyline_1"
android:contentDescription="@string/album_art"
+ android:background="@color/car_body1_light"
android:scaleType="centerCrop"
android:transitionName="@string/album_art"
app:layout_constraintTop_toTopOf="parent"
diff --git a/res/layout/fragment_playback_with_queue.xml b/res/layout-h1200dp/fragment_playback_with_queue.xml
index 113de90..113de90 100644
--- a/res/layout/fragment_playback_with_queue.xml
+++ b/res/layout-h1200dp/fragment_playback_with_queue.xml
diff --git a/res/layout/browse_state.xml b/res/layout/browse_state.xml
new file mode 100644
index 0000000..f85b1a2
--- /dev/null
+++ b/res/layout/browse_state.xml
@@ -0,0 +1,51 @@
+<?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.
+-->
+<merge
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:showIn="@layout/fragment_browse">
+
+ <ProgressBar
+ android:id="@+id/loading_spinner"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/car_double_line_list_item_height"
+ android:layout_centerInParent="true"
+ android:visibility="gone"
+ android:indeterminateDrawable="@drawable/music_buffering" />
+
+ <ImageView
+ android:id="@+id/error_icon"
+ android:layout_width="@dimen/missing_permission_icon_size"
+ android:layout_height="@dimen/missing_permission_icon_size"
+ android:layout_centerInParent="true"
+ android:src="@drawable/error_illustration"
+ android:visibility="gone"
+ android:tint="@color/car_body2_light"/>
+
+ <TextView
+ android:id="@+id/error_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/error_icon"
+ android:layout_marginTop="@dimen/car_padding_4"
+ android:textAppearance="@style/TextAppearance.Car.Body2.Light"
+ android:gravity="center"
+ android:maxLines="3"
+ android:text="@string/nothing_to_play"
+ android:visibility="gone" />
+
+</merge> \ No newline at end of file
diff --git a/res/layout/fragment_browse.xml b/res/layout/fragment_browse.xml
index 30dcf03..9e9eca5 100644
--- a/res/layout/fragment_browse.xml
+++ b/res/layout/fragment_browse.xml
@@ -21,33 +21,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <ProgressBar
- android:id="@+id/loading_spinner"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_double_line_list_item_height"
- android:layout_centerInParent="true"
- android:layout_alignWithParentIfMissing="true"
- android:indeterminateDrawable="@drawable/music_buffering"
- android:visibility="gone" />
-
- <ImageView
- android:id="@+id/error_icon"
- android:layout_width="@dimen/missing_permission_icon_size"
- android:layout_height="@dimen/missing_permission_icon_size"
- android:layout_centerInParent="true"
- android:src="@drawable/error_illustration"
- android:visibility="gone" />
-
- <TextView
- android:id="@+id/error_message"
- style="@style/TextAppearance.NoContent"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@+id/error_icon"
- android:gravity="center"
- android:maxLines="3"
- android:text="@string/nothing_to_play"
- android:visibility="gone" />
+ <include layout="@layout/browse_state"/>
<androidx.car.widget.PagedListView
android:id="@+id/browse_list"
diff --git a/res/layout/fragment_empty.xml b/res/layout/fragment_empty.xml
index a7d7a53..7f53519 100644
--- a/res/layout/fragment_empty.xml
+++ b/res/layout/fragment_empty.xml
@@ -16,16 +16,10 @@
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/browse_container"
+ android:id="@+id/empty_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <ProgressBar
- android:id="@+id/loading_spinner"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_double_line_list_item_height"
- android:layout_centerInParent="true"
- android:visibility="gone"
- android:indeterminateDrawable="@drawable/music_buffering" />
+ <include layout="@layout/browse_state"/>
</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/fragment_metadata.xml b/res/layout/fragment_metadata.xml
index ffeaf95..4324681 100644
--- a/res/layout/fragment_metadata.xml
+++ b/res/layout/fragment_metadata.xml
@@ -18,22 +18,21 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
- tools:showIn="@layout/fragment_playback">
+ tools:showIn="@layout/fragment_playback_with_queue">
+
<ImageView
android:id="@+id/album_art"
- android:layout_width="@dimen/playback_album_art_size_large"
- android:layout_height="@dimen/playback_album_art_size_large"
- android:layout_marginEnd="@dimen/car_keyline_2"
- android:layout_marginStart="@dimen/car_keyline_2"
+ android:layout_width="@dimen/playback_album_art_size_normal"
+ android:layout_height="@dimen/playback_album_art_size_normal"
+ android:layout_marginStart="@dimen/car_keyline_1"
android:contentDescription="@string/album_art"
+ android:background="@color/car_body1_light"
android:scaleType="centerCrop"
android:transitionName="@string/album_art"
- app:layout_constraintBottom_toTopOf="@id/metadata_subcontainer"
- app:layout_constraintEnd_toEndOf="@+id/margin_end"
- app:layout_constraintStart_toStartOf="@+id/margin_start"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_chainStyle="packed"
+ app:layout_constraintBottom_toTopOf="@+id/playback_controls"
+ app:layout_constraintStart_toStartOf="@+id/margin_start"
tools:src="@drawable/ic_person"/>
<include
@@ -41,24 +40,25 @@
layout="@layout/metadata_normal"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/car_padding_5"
- android:layout_marginBottom="@dimen/playback_controls_margin"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="@+id/album_art"
- app:layout_constraintStart_toStartOf="@+id/album_art"
- app:layout_constraintTop_toBottomOf="@+id/album_art"/>
+ android:layout_marginStart="@dimen/car_padding_4"
+ android:layout_marginEnd="@dimen/car_keyline_1"
+ app:layout_constraintBottom_toBottomOf="@+id/album_art"
+ app:layout_constraintEnd_toEndOf="@+id/margin_end"
+ app:layout_constraintStart_toEndOf="@+id/album_art"
+ app:layout_constraintTop_toTopOf="@+id/album_art"/>
<androidx.car.widget.PagedListView
android:id="@+id/queue_list"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_marginTop="@dimen/playback_album_art_size_normal"
+ android:layout_marginTop="@dimen/car_padding_4"
android:layout_marginBottom="@dimen/playback_controls_margin"
android:visibility="gone"
app:dividerEndMargin="@dimen/car_keyline_1"
app:dividerStartMargin="@dimen/car_keyline_1"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/margin_top"
+ app:layout_constraintTop_toBottomOf="@+id/album_art"
app:listDividerColor="@color/car_list_divider_light"/>
+
</merge>
diff --git a/res/layout/media_activity.xml b/res/layout/media_activity.xml
index 7a45458..050a9c8 100644
--- a/res/layout/media_activity.xml
+++ b/res/layout/media_activity.xml
@@ -39,8 +39,7 @@
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_marginTop="@dimen/car_padding_4"
- android:visibility="gone"
+ android:visibility="visible"
app:layout_constraintTop_toBottomOf="@+id/app_bar"
app:layout_constraintBottom_toTopOf="@+id/browse_controls_container"/>
@@ -48,7 +47,6 @@
android:id="@+id/playback_container"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_marginTop="@dimen/car_padding_4"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@+id/app_bar"
app:layout_constraintBottom_toBottomOf="parent"/>
@@ -86,7 +84,7 @@
<com.android.car.media.widgets.AppBarView
android:id="@+id/app_bar"
- android:layout_height="@dimen/appbar_height"
+ android:layout_height="@dimen/car_app_bar_height"
android:layout_width="match_parent"
app:layout_constraintTop_toTopOf="parent"/>
diff --git a/res/values-h1200dp/bools.xml b/res/values-h1200dp/bools.xml
index 34eae64..b1476c8 100644
--- a/res/values-h1200dp/bools.xml
+++ b/res/values-h1200dp/bools.xml
@@ -15,6 +15,6 @@
limitations under the License.
-->
<resources>
+ <!-- On screens tall enough we enable content forward browsing -->
<bool name="forward_content_browse_enabled">true</bool>
- <bool name="force_browse_tabs">true</bool>
</resources> \ No newline at end of file
diff --git a/res/values-h600dp/dimens.xml b/res/values-h1200dp/dimens.xml
index 25639cf..6032220 100644
--- a/res/values-h600dp/dimens.xml
+++ b/res/values-h1200dp/dimens.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!-- Copyright (C) 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.
@@ -14,5 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
- <dimen name="now_playing_metadata_top_margin">32dp</dimen>
+ <!-- Application Bar -->
+ <dimen name="car_app_bar_height">116dp</dimen>
</resources>
diff --git a/res/values/bools.xml b/res/values/bools.xml
index b9bd29e..3247ad9 100644
--- a/res/values/bools.xml
+++ b/res/values/bools.xml
@@ -17,8 +17,4 @@
<resources>
<!-- Whether forward content browse is enabled -->
<bool name="forward_content_browse_enabled">false</bool>
-
- <!-- Force the presentation of tabs even if the number of browsable items exceeds the
- maximum number of allowed tabs (this is mainly for demo purposes) -->
- <bool name="force_browse_tabs">false</bool>
</resources> \ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index f06b143..f4a7439 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -23,9 +23,8 @@
<!-- Music now playing screen -->
<dimen name="music_action_icon_inset">0dp</dimen>
<dimen name="music_action_ripple_inset">32dp</dimen>
- <dimen name="now_playing_metadata_top_margin">0dp</dimen>
- <dimen name="missing_permission_icon_size">208dp</dimen>
+ <dimen name="missing_permission_icon_size">128dp</dimen>
<dimen name="controls_tap_target_width">64dp</dimen>
<dimen name="controls_tap_target_height">64dp</dimen>
diff --git a/res/values/integers.xml b/res/values/integers.xml
index 8a70b8a..d0f23ea 100644
--- a/res/values/integers.xml
+++ b/res/values/integers.xml
@@ -34,5 +34,5 @@
<integer name="app_selector_fade_duration">500</integer>
<!-- Time allowed for a process to complete before we show a progress indicator -->
- <integer name="progress_indicator_delay">500</integer>
+ <integer name="progress_indicator_delay">1000</integer>
</resources>
diff --git a/src/com/android/car/media/BrowseFragment.java b/src/com/android/car/media/BrowseFragment.java
index 1a6571d..ecb3f6c 100644
--- a/src/com/android/car/media/BrowseFragment.java
+++ b/src/com/android/car/media/BrowseFragment.java
@@ -90,14 +90,19 @@ public class BrowseFragment extends Fragment {
mBrowseAdapter.update();
if (mBrowseAdapter.getItemCount() > 0) {
ViewUtils.showViewAnimated(mBrowseList, mFadeDuration);
+ ViewUtils.hideViewAnimated(mErrorIcon, mFadeDuration);
+ ViewUtils.hideViewAnimated(mErrorMessage, mFadeDuration);
} else {
mErrorMessage.setText(R.string.nothing_to_play);
+ ViewUtils.hideViewAnimated(mBrowseList, mFadeDuration);
+ ViewUtils.hideViewAnimated(mErrorIcon, mFadeDuration);
ViewUtils.showViewAnimated(mErrorMessage, mFadeDuration);
}
break;
case ERROR:
stopLoadingIndicator();
mErrorMessage.setText(R.string.unknown_error);
+ ViewUtils.hideViewAnimated(mBrowseList, mFadeDuration);
ViewUtils.showViewAnimated(mErrorMessage, mFadeDuration);
ViewUtils.showViewAnimated(mErrorIcon, mFadeDuration);
break;
@@ -167,6 +172,7 @@ public class BrowseFragment extends Fragment {
/**
* Creates a new instance of this fragment.
*
+ * @param mediaSource media source being displayed
* @param item media tree node to display on this fragment.
* @return a fully initialized {@link BrowseFragment}
*/
@@ -244,9 +250,6 @@ public class BrowseFragment extends Fragment {
if (mMediaSource != null) {
mMediaSource.subscribe(mBrowseObserver);
}
- if (mBrowseAdapter != null) {
- mBrowseAdapter.start();
- }
}
private Runnable mProgressIndicatorRunnable = new Runnable() {
@@ -276,6 +279,7 @@ public class BrowseFragment extends Fragment {
}
if (mBrowseAdapter != null) {
mBrowseAdapter.stop();
+ mBrowseAdapter = null;
}
}
@@ -299,8 +303,8 @@ public class BrowseFragment extends Fragment {
ViewUtils.showViewAnimated(mErrorMessage, mFadeDuration);
return;
}
- mBrowseAdapter = new BrowseAdapter(getContext(), mMediaSource.getMediaBrowser(),
- getCurrentMediaItem(), ContentForwardStrategy.DEFAULT_STRATEGY);
+ mBrowseAdapter = new BrowseAdapter(getContext(), mMediaSource, getCurrentMediaItem(),
+ ContentForwardStrategy.DEFAULT_STRATEGY);
mBrowseList.setAdapter(mBrowseAdapter);
mBrowseList.setDividerVisibilityManager(mBrowseAdapter);
mBrowseAdapter.registerObserver(mBrowseAdapterObserver);
diff --git a/src/com/android/car/media/EmptyFragment.java b/src/com/android/car/media/EmptyFragment.java
index bbdfe6d..72b6889 100644
--- a/src/com/android/car/media/EmptyFragment.java
+++ b/src/com/android/car/media/EmptyFragment.java
@@ -3,11 +3,15 @@ package com.android.car.media;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.ProgressBar;
+import android.widget.TextView;
+import com.android.car.media.common.MediaSource;
import com.android.car.media.widgets.ViewUtils;
/**
@@ -15,16 +19,20 @@ import com.android.car.media.widgets.ViewUtils;
*/
public class EmptyFragment extends Fragment {
private ProgressBar mProgressBar;
+ private ImageView mErrorIcon;
+ private TextView mErrorMessage;
+
private int mProgressBarDelay;
private Handler mHandler = new Handler();
private int mFadeDuration;
+ private MediaActivity.BrowseState mState = MediaActivity.BrowseState.EMPTY;
+ private MediaSource mMediaSource;
private Runnable mProgressIndicatorRunnable = new Runnable() {
@Override
public void run() {
ViewUtils.showViewAnimated(mProgressBar, mFadeDuration);
}
};
-
@Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
@@ -34,27 +42,61 @@ public class EmptyFragment extends Fragment {
.getInteger(R.integer.progress_indicator_delay);
mFadeDuration = getContext().getResources().getInteger(
R.integer.new_album_art_fade_in_duration);
+ mErrorIcon = view.findViewById(R.id.error_icon);
+ mErrorMessage = view.findViewById(R.id.error_message);
+ update();
return view;
}
@Override
- public void onStart() {
- super.onStart();
- mProgressBar.setVisibility(View.GONE);
+ public void onPause() {
+ super.onPause();
+ mHandler.removeCallbacks(mProgressIndicatorRunnable);
}
- @Override
- public void onResume() {
- super.onResume();
- // Display the indicator after a certain time, to avoid flashing the indicator constantly,
- // even when performance is acceptable.
- mHandler.postDelayed(mProgressIndicatorRunnable, mProgressBarDelay);
+ /**
+ * Updates the state of this fragment
+ *
+ * @param state browsing state to display
+ * @param mediaSource media source currently being browsed
+ */
+ public void setState(MediaActivity.BrowseState state, MediaSource mediaSource) {
+ mHandler.removeCallbacks(mProgressIndicatorRunnable);
+ mMediaSource = mediaSource;
+ mState = state;
+ if (this.getView() != null) {
+ update();
+ }
}
- @Override
- public void onPause() {
- super.onPause();
- mHandler.removeCallbacks(mProgressIndicatorRunnable);
- mProgressBar.setVisibility(View.GONE);
+ private void update() {
+ switch (mState) {
+ case LOADING:
+ // Display the indicator after a certain time, to avoid flashing the indicator
+ // constantly, even when performance is acceptable.
+ mHandler.postDelayed(mProgressIndicatorRunnable, mProgressBarDelay);
+ mErrorIcon.setVisibility(View.GONE);
+ mErrorMessage.setVisibility(View.GONE);
+ break;
+ case ERROR:
+ mProgressBar.setVisibility(View.GONE);
+ mErrorIcon.setVisibility(View.VISIBLE);
+ mErrorMessage.setVisibility(View.VISIBLE);
+ mErrorMessage.setText(getContext().getString(
+ R.string.cannot_connect_to_app,
+ mMediaSource != null
+ ? mMediaSource.getName()
+ : getContext().getString(R.string.unknown_media_provider_name)));
+ break;
+ case EMPTY:
+ mProgressBar.setVisibility(View.GONE);
+ mErrorIcon.setVisibility(View.GONE);
+ mErrorMessage.setVisibility(View.VISIBLE);
+ mErrorMessage.setText(getContext().getString(R.string.nothing_to_play));
+ break;
+ default:
+ // Fail fast on any other state.
+ throw new IllegalStateException("Invalid state for this fragment: " + mState);
+ }
}
}
diff --git a/src/com/android/car/media/MediaActivity.java b/src/com/android/car/media/MediaActivity.java
index 00f53fc..d7b809b 100644
--- a/src/com/android/car/media/MediaActivity.java
+++ b/src/com/android/car/media/MediaActivity.java
@@ -23,9 +23,9 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.os.Bundle;
-import android.support.design.widget.AppBarLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
+import android.support.v4.widget.DrawerLayout;
import android.transition.Fade;
import android.util.Log;
import android.util.TypedValue;
@@ -56,7 +56,7 @@ import androidx.car.drawer.CarDrawerAdapter;
* by broadcast. Drawer menu is controlled by {@link MediaDrawerController}.
*/
public class MediaActivity extends CarDrawerActivity implements BrowseFragment.Callbacks,
- AppSelectionFragment.Callbacks {
+ AppSelectionFragment.Callbacks, PlaybackFragment.Callbacks {
private static final String TAG = "MediaActivity";
/** Intent extra specifying the package with the MediaBrowser */
@@ -84,11 +84,10 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
private CrossfadeImageView mAlbumBackground;
private PlaybackFragment mPlaybackFragment;
private AppSelectionFragment mAppSelectionFragment;
- private AppBarLayout mDrawerBarLayout;
private PlaybackControls mPlaybackControls;
private MetadataView mMetadataView;
private ViewGroup mBrowseControlsContainer;
- private Fragment mEmptyFragment;
+ private EmptyFragment mEmptyFragment;
private ViewGroup mBrowseContainer;
private ViewGroup mPlaybackContainer;
@@ -109,8 +108,6 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
MediaActivity.this.onBrowseConnected(false);
}
};
- private MediaSource.ItemsSubscription mRootItemsSubscription =
- (parentId, items) -> updateTabs(items);
private PlaybackModel.PlaybackObserver mPlaybackObserver =
new PlaybackModel.PlaybackObserver() {
@Override
@@ -124,10 +121,23 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
updateMetadata();
}
};
+ private MediaSource.ItemsSubscription mItemsSubscription =
+ new MediaSource.ItemsSubscription() {
+ @Override
+ public void onChildrenLoaded(MediaSource mediaSource, String parentId,
+ List<MediaItemMetadata> items) {
+ if (mediaSource == mMediaSource) {
+ updateTabs(items);
+ } else {
+ Log.w(TAG, "Received items for a wrong source: " +
+ mediaSource.getPackageName());
+ }
+ }
+ };
private AppBarView.AppBarListener mAppBarListener = new AppBarView.AppBarListener() {
@Override
public void onTabSelected(MediaItemMetadata item) {
- updateBrowseFragment(item, false);
+ updateBrowseFragment(BrowseState.LOADED, item);
switchToMode(Mode.BROWSING);
}
@@ -146,6 +156,7 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
@Override
public void onAppSelection() {
+ Log.d(TAG, "onAppSelection clicked");
if (mIsAppSelectorOpen) {
closeAppSelector();
} else {
@@ -157,28 +168,68 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
mAppBarView.setAppSelection(!mMediaSourcesManager.getMediaSources().isEmpty());
mAppSelectionFragment.refresh();
};
+ private DrawerLayout.DrawerListener mDrawerListener = new DrawerLayout.DrawerListener() {
+ @Override
+ public void onDrawerSlide(@android.support.annotation.NonNull View view, float v) {
+ }
+
+ @Override
+ public void onDrawerOpened(@android.support.annotation.NonNull View view) {
+ closeAppSelector();
+ }
+
+ @Override
+ public void onDrawerClosed(@android.support.annotation.NonNull View view) {
+ }
+
+ @Override
+ public void onDrawerStateChanged(int i) {
+ }
+ };
+ /**
+ * Possible modes of the application UI
+ */
private enum Mode {
+ /** The user is browsing a media source */
BROWSING,
+ /** The user is interacting with the full screen playback UI */
PLAYBACK
}
+ /**
+ * Possible states of the application UI
+ */
+ public enum BrowseState {
+ /** There is no content to show */
+ EMPTY,
+ /** We are still in the process of obtaining data */
+ LOADING,
+ /** Data has been loaded */
+ LOADED,
+ /** The content can't be shown due an error */
+ ERROR
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setMainContent(R.layout.media_activity);
-
setToolbarElevation(0f);
+ setToolbarClickThrough(true);
mContentForwardBrowseEnabled = getResources()
.getBoolean(R.bool.forward_content_browse_enabled);
mDrawerController = new MediaDrawerController(this, getDrawerController());
getDrawerController().setRootAdapter(getRootAdapter());
+ getDrawerController().addDrawerListener(mDrawerListener);
+ if (mContentForwardBrowseEnabled) {
+ getSupportActionBar().hide();
+ }
mAppBarView = findViewById(R.id.app_bar);
mAppBarView.setListener(mAppBarListener);
- boolean forceBrowseTabs = getResources().getBoolean(R.bool.force_browse_tabs);
- mAppBarView.setVisibility(forceBrowseTabs ? View.VISIBLE : View.GONE);
+ mAppBarView.setContentForwardEnabled(mContentForwardBrowseEnabled);
mPlaybackFragment = new PlaybackFragment();
mAppSelectionFragment = new AppSelectionFragment();
int fadeDuration = getResources().getInteger(R.integer.app_selector_fade_duration);
@@ -186,8 +237,6 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
mAppSelectionFragment.setExitTransition(new Fade().setDuration(fadeDuration));
mPlaybackModel = new PlaybackModel(this);
mMediaSourcesManager = new MediaSourcesManager(this);
- mDrawerBarLayout = findViewById(androidx.car.R.id.appbar);
- mDrawerBarLayout.setVisibility(forceBrowseTabs ? View.GONE : View.VISIBLE);
mAlbumBackground = findViewById(R.id.media_background);
mPlaybackControls = findViewById(R.id.browse_controls);
mPlaybackControls.setModel(mPlaybackModel);
@@ -216,6 +265,7 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
super.onResume();
mPlaybackModel.registerObserver(mPlaybackObserver);
mMediaSourcesManager.registerObserver(mMediaSourcesManagerObserver);
+ handleIntent();
}
@Override
@@ -255,19 +305,15 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
super.onBackPressed();
}
- @Override
- protected void onResumeFragments() {
- super.onResumeFragments();
- handleIntent();
- }
-
private void onBrowseConnected(boolean success) {
if (!success) {
- updateTabs(new ArrayList<>());
- mMediaSource.unsubscribeChildren(null);
+ updateTabs(null);
+ mMediaSource.unsubscribeChildren(null, mItemsSubscription);
+ mMediaSource.unsubscribe(mMediaSourceObserver);
+ updateBrowseFragment(BrowseState.ERROR, null);
return;
}
- mMediaSource.subscribeChildren(null, mRootItemsSubscription);
+ mMediaSource.subscribeChildren(null, mItemsSubscription);
}
private void handleIntent() {
@@ -303,7 +349,6 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
// and display what we have.
if (mMediaSource != null) {
closeAppSelector();
- updateMetadata();
return;
}
@@ -312,7 +357,7 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
if (lastMediaSource != null) {
closeAppSelector();
updateBrowseSource(lastMediaSource);
- updateMetadata();
+ switchToMode(Mode.BROWSING);
} else {
// If we don't have anything from before: open the app selector.
openAppSelector();
@@ -329,13 +374,12 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
return;
}
if (mMediaSource != null) {
- mMediaSource.unsubscribeChildren(null);
+ mMediaSource.unsubscribeChildren(null, mItemsSubscription);
mMediaSource.unsubscribe(mMediaSourceObserver);
updateTabs(new ArrayList<>());
}
mMediaSource = mediaSource;
setLastMediaSource(mMediaSource);
- mAppBarView.setState(AppBarView.State.BROWSING);
if (mMediaSource != null) {
if (Log.isLoggable(TAG, Log.INFO)) {
Log.i(TAG, "Browsing: " + mediaSource.getName());
@@ -344,10 +388,10 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
MediaManager.getInstance(this).setMediaClientComponent(component);
// If content forward browsing is disabled, then no need to subscribe to this media
// source.
- updateBrowseFragment(null, true);
if (mContentForwardBrowseEnabled) {
Log.i(TAG, "Content forward is enabled: subscribing to " +
mMediaSource.getPackageName());
+ updateBrowseFragment(BrowseState.LOADING, null);
mMediaSource.subscribe(mMediaSourceObserver);
}
mAppBarView.setAppIcon(mMediaSource.getRoundPackageIcon());
@@ -358,21 +402,26 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
}
}
- /**
- * @return the package name of the media source requested by the incoming {@link Intent} or
- * null if no source was indicated.
- */
- private String getRequestedMediaPackageName() {
- return getIntent() != null
- ? getIntent().getStringExtra(KEY_MEDIA_PACKAGE)
- : null;
- }
-
private boolean isCurrentMediaSourcePlaying() {
return Objects.equals(mMediaSource, mPlaybackModel.getMediaSource());
}
+ /**
+ * Updates the tabs displayed on the app bar, based on the top level items on the browse tree.
+ * If there is at least one browsable item, we show the browse content of that node.
+ * If there are only playable items, then we show those items.
+ * If there are not items at all, we show the empty message.
+ * If we receive null, we show the error message.
+ *
+ * @param items top level items, or null if there was an error trying load those items.
+ */
private void updateTabs(List<MediaItemMetadata> items) {
+ if (items == null || items.isEmpty()) {
+ mAppBarView.setItems(null);
+ updateBrowseFragment(items == null ? BrowseState.ERROR : BrowseState.EMPTY, null);
+ return;
+ }
+
items = customizeTabs(mMediaSource, items);
List<MediaItemMetadata> browsableTopLevel = items.stream()
.filter(item -> item.isBrowsable())
@@ -381,11 +430,11 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
if (!browsableTopLevel.isEmpty()) {
// If we have at least a few browsable items, we show the tabs
mAppBarView.setItems(browsableTopLevel);
- updateBrowseFragment(browsableTopLevel.get(0), false);
+ updateBrowseFragment(BrowseState.LOADED, browsableTopLevel.get(0));
} else {
// Otherwise, we show the top of the tree with no fabs
mAppBarView.setItems(null);
- updateBrowseFragment(null, false);
+ updateBrowseFragment(BrowseState.LOADED, null);
}
}
@@ -404,9 +453,11 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
}
private void switchToMode(Mode mode) {
- mMode = mode;
+ // If content forward is not enable, then we always show the playback UI (browse will be
+ // done in the drawer)
+ mMode = mContentForwardBrowseEnabled ? mode : Mode.PLAYBACK;
updateMetadata();
- switch (mode) {
+ switch (mMode) {
case PLAYBACK:
ViewUtils.showViewAnimated(mPlaybackContainer, mFadeDuration);
ViewUtils.hideViewAnimated(mBrowseContainer, mFadeDuration);
@@ -424,23 +475,32 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
* Updates the browse area with either a loading state, the root node content, or the
* content of a particular media item.
*
- * @param topItem item to display, or null to display the root node.
- * @param isLoading whether we should show the loading state.
+ * @param state state in the process of loading browse information.
+ * @param topItem if state == IDLE, this will contain the item to display,
+ * or null to display the root node.
*/
- private void updateBrowseFragment(MediaItemMetadata topItem, boolean isLoading) {
- if (isLoading) {
- mCurrentFragment = mEmptyFragment;
- mAppBarView.setActiveItem(null);
- } else if (topItem != null) {
- mCurrentFragment = BrowseFragment.newInstance(mMediaSource, topItem);
- mAppBarView.setActiveItem(topItem);
- } else {
- mCurrentFragment = BrowseFragment.newInstance(mMediaSource, null);
- mAppBarView.setActiveItem(null);
+ private void updateBrowseFragment(BrowseState state, MediaItemMetadata topItem) {
+ switch(state) {
+ case LOADED:
+ if (topItem != null) {
+ mCurrentFragment = BrowseFragment.newInstance(mMediaSource, topItem);
+ mAppBarView.setActiveItem(topItem);
+ } else {
+ mCurrentFragment = BrowseFragment.newInstance(mMediaSource, null);
+ mAppBarView.setActiveItem(null);
+ }
+ break;
+ case EMPTY:
+ case LOADING:
+ case ERROR:
+ mCurrentFragment = mEmptyFragment;
+ mEmptyFragment.setState(state, mMediaSource);
+ mAppBarView.setActiveItem(null);
+ break;
}
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, mCurrentFragment)
- .commit();
+ .commitAllowingStateLoss();
}
private void updateMetadata() {
@@ -536,7 +596,8 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
private void closeAppSelector() {
mIsAppSelectorOpen = false;
FragmentManager manager = getSupportFragmentManager();
- mAppBarView.setState(AppBarView.State.BROWSING);
+ mAppBarView.setState(mMode == Mode.PLAYBACK ? AppBarView.State.PLAYING
+ : AppBarView.State.BROWSING);
manager.beginTransaction()
.remove(mAppSelectionFragment)
.commit();
@@ -554,6 +615,7 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
public void onMediaSourceSelected(MediaSource mediaSource) {
closeAppSelector();
if (mediaSource.getMediaBrowser() != null && !mediaSource.isCustom()) {
+ mCurrentMetadata = null;
updateBrowseSource(mediaSource);
switchToMode(Mode.BROWSING);
} else {
@@ -582,4 +644,14 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
.putString(LAST_MEDIA_SOURCE_SHARED_PREF_KEY, mediaSource.getPackageName())
.apply();
}
+
+
+ @Override
+ public void onQueueButtonClicked() {
+ if (mContentForwardBrowseEnabled) {
+ mPlaybackFragment.toggleQueueVisibility();
+ } else {
+ mDrawerController.showPlayQueue();
+ }
+ }
}
diff --git a/src/com/android/car/media/MetadataController.java b/src/com/android/car/media/MetadataController.java
index 5f74891..e7261f5 100644
--- a/src/com/android/car/media/MetadataController.java
+++ b/src/com/android/car/media/MetadataController.java
@@ -14,7 +14,6 @@ import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
-import java.util.Objects;
/**
* Common controller for displaying current track's metadata.
@@ -116,9 +115,13 @@ public class MetadataController {
MediaItemMetadata metadata = mModel != null ? mModel.getMetadata() : null;
mTitle.setText(metadata != null ? metadata.getTitle() : null);
mSubtitle.setText(metadata != null ? metadata.getSubtitle() : null);
- if (mAlbumArt != null && metadata != null) {
+ if (mAlbumArt != null && metadata != null && (metadata.getAlbumArtUri() != null
+ || metadata.getAlbumArtBitmap() != null)) {
+ mAlbumArt.setVisibility(View.VISIBLE);
metadata.getAlbumArt(mAlbumArt.getContext(), mAlbumArtSize, mAlbumArtSize, true)
.thenAccept(mAlbumArt::setImageBitmap);
+ } else if (mAlbumArt != null) {
+ mAlbumArt.setVisibility(View.GONE);
}
}
diff --git a/src/com/android/car/media/PlaybackFragment.java b/src/com/android/car/media/PlaybackFragment.java
index da42832..d88a348 100644
--- a/src/com/android/car/media/PlaybackFragment.java
+++ b/src/com/android/car/media/PlaybackFragment.java
@@ -61,6 +61,7 @@ public class PlaybackFragment extends Fragment {
private PlaybackControls mPlaybackControls;
private QueueItemsAdapter mQueueAdapter;
private PagedListView mQueue;
+ private Callbacks mCallbacks;
private MetadataController mMetadataController;
private ConstraintLayout mRootView;
@@ -123,12 +124,22 @@ public class PlaybackFragment extends Fragment {
}
}
+ /**
+ * Callbacks this fragment can trigger
+ */
+ public interface Callbacks {
+ /**
+ * Indicates that the "show queue" button has been clicked
+ */
+ void onQueueButtonClicked();
+ }
+
private PlaybackControls.Listener mPlaybackControlsListener = new PlaybackControls.Listener() {
@Override
public void onToggleQueue() {
- mQueueIsVisible = !mQueueIsVisible;
- mPlaybackControls.setQueueVisible(mQueueIsVisible);
- setQueueVisible(mQueueIsVisible);
+ if (mCallbacks != null) {
+ mCallbacks.onQueueButtonClicked();
+ }
}
};
@@ -146,6 +157,18 @@ public class PlaybackFragment extends Fragment {
return view;
}
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ mCallbacks = (Callbacks) context;
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ mCallbacks = null;
+ }
+
private void initPlaybackControls(PlaybackControls playbackControls) {
mPlaybackControls = playbackControls;
mPlaybackControls.setModel(mModel);
@@ -193,9 +216,15 @@ public class PlaybackFragment extends Fragment {
mModel.unregisterObserver(mPlaybackObserver);
}
- public void setQueueVisible(boolean visible) {
+ /**
+ * Hides or shows the playback queue
+ */
+ public void toggleQueueVisibility() {
+ mQueueIsVisible = !mQueueIsVisible;
+ mPlaybackControls.setQueueVisible(mQueueIsVisible);
+
Transition transition = TransitionInflater.from(getContext()).inflateTransition(
- visible ? R.transition.queue_in : R.transition.queue_out);
+ mQueueIsVisible ? R.transition.queue_in : R.transition.queue_out);
transition.addListener(new TransitionListenerAdapter() {
@Override
@@ -218,9 +247,8 @@ public class PlaybackFragment extends Fragment {
TransitionManager.beginDelayedTransition(mRootView, transition);
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(mRootView.getContext(),
- visible ? R.layout.fragment_playback_with_queue : R.layout.fragment_playback);
+ mQueueIsVisible ? R.layout.fragment_playback_with_queue : R.layout.fragment_playback);
constraintSet.applyTo(mRootView);
-
}
private void updateState() {
diff --git a/src/com/android/car/media/browse/BrowseAdapter.java b/src/com/android/car/media/browse/BrowseAdapter.java
index 5d2bff0..dd5d2ee 100644
--- a/src/com/android/car/media/browse/BrowseAdapter.java
+++ b/src/com/android/car/media/browse/BrowseAdapter.java
@@ -30,6 +30,7 @@ import android.view.View;
import android.view.ViewGroup;
import com.android.car.media.common.MediaItemMetadata;
+import com.android.car.media.common.MediaSource;
import java.util.ArrayList;
import java.util.Collection;
@@ -69,7 +70,7 @@ public class BrowseAdapter extends RecyclerView.Adapter<BrowseViewHolder> implem
private static final String TAG = "BrowseAdapter";
@NonNull
private final Context mContext;
- private final MediaBrowser mMediaBrowser;
+ private final MediaSource mMediaSource;
private final ContentForwardStrategy mCFBStrategy;
private MediaItemMetadata mParentMediaItem;
private LinkedHashMap<String, MediaItemState> mItemStates = new LinkedHashMap<>();
@@ -131,29 +132,14 @@ public class BrowseAdapter extends RecyclerView.Adapter<BrowseViewHolder> implem
protected void onQueueItemClicked(MediaItemMetadata item) {};
}
- private MediaBrowser.SubscriptionCallback mSubscriptionCallback =
- new MediaBrowser.SubscriptionCallback() {
- @Override
- public void onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children) {
- onItemsLoaded(parentId, children);
- }
-
- @Override
- public void onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children,
- Bundle options) {
- onItemsLoaded(parentId, children);
- }
-
- @Override
- public void onError(String parentId) {
- onLoadingError(parentId);
- }
-
- @Override
- public void onError(String parentId, Bundle options) {
- onLoadingError(parentId);
- }
- };
+ private MediaSource.ItemsSubscription mSubscriptionCallback =
+ (mediaSource, parentId, items) -> {
+ if (items != null) {
+ onItemsLoaded(parentId, items);
+ } else {
+ onLoadingError(parentId);
+ }
+ };
/**
@@ -174,19 +160,19 @@ public class BrowseAdapter extends RecyclerView.Adapter<BrowseViewHolder> implem
/** Whether we are subscribed to updates for this item or not */
boolean mIsSubscribed;
- MediaItemState(MediaBrowser.MediaItem item) {
- mItem = new MediaItemMetadata(item);
+ MediaItemState(MediaItemMetadata item) {
+ mItem = item;
}
- void setChildren(List<MediaBrowser.MediaItem> children) {
+ void setChildren(List<MediaItemMetadata> children) {
mPlayableChildren.clear();
mBrowsableChildren.clear();
- for (MediaBrowser.MediaItem child : children) {
+ for (MediaItemMetadata child : children) {
if (child.isBrowsable()) {
// Browsable items could also be playable
- mBrowsableChildren.add(new MediaItemMetadata(child));
+ mBrowsableChildren.add(child);
} else if (child.isPlayable()) {
- mPlayableChildren.add(new MediaItemMetadata(child));
+ mPlayableChildren.add(child);
}
}
}
@@ -195,15 +181,15 @@ public class BrowseAdapter extends RecyclerView.Adapter<BrowseViewHolder> implem
/**
* Creates a {@link BrowseAdapter} that displays the children of the given media tree node.
*
- * @param mediaBrowser the {@link MediaBrowser} to get data from.
+ * @param mediaSource the {@link MediaSource} to get data from.
* @param parentItem the node to display children of, or NULL if the
* @param strategy a {@link ContentForwardStrategy} that would determine which items would be
* expanded and how.
*/
- public BrowseAdapter(Context context, @NonNull MediaBrowser mediaBrowser,
+ public BrowseAdapter(Context context, @NonNull MediaSource mediaSource,
@Nullable MediaItemMetadata parentItem, @NonNull ContentForwardStrategy strategy) {
mContext = context;
- mMediaBrowser = mediaBrowser;
+ mMediaSource = mediaSource;
mParentMediaItem = parentItem;
mCFBStrategy = strategy;
}
@@ -213,10 +199,9 @@ public class BrowseAdapter extends RecyclerView.Adapter<BrowseViewHolder> implem
* {@link #registerObserver(Observer)} to receive updates on the progress.
*/
public void start() {
- mParentMediaItemId = mParentMediaItem != null
- ? mParentMediaItem.getId()
- : mMediaBrowser.getRoot();
- mMediaBrowser.subscribe(mParentMediaItemId, mSubscriptionCallback);
+ mParentMediaItemId = mParentMediaItem != null ? mParentMediaItem.getId() :
+ mMediaSource.getRoot();
+ mMediaSource.subscribeChildren(mParentMediaItemId, mSubscriptionCallback);
for (MediaItemState itemState : mItemStates.values()) {
subscribe(itemState);
}
@@ -230,7 +215,7 @@ public class BrowseAdapter extends RecyclerView.Adapter<BrowseViewHolder> implem
// Not started
return;
}
- mMediaBrowser.unsubscribe(mParentMediaItemId, mSubscriptionCallback);
+ mMediaSource.unsubscribeChildren(mParentMediaItemId, mSubscriptionCallback);
for (MediaItemState itemState : mItemStates.values()) {
unsubscribe(itemState);
}
@@ -244,16 +229,15 @@ public class BrowseAdapter extends RecyclerView.Adapter<BrowseViewHolder> implem
* @param parentItem new media item to expand.
*/
public void setParentMediaItemId(@Nullable MediaItemMetadata parentItem) {
- String newParentMediaItemId = parentItem != null
- ? parentItem.getId()
- : mMediaBrowser.getRoot();
+ String newParentMediaItemId = parentItem != null ? parentItem.getId() :
+ mMediaSource.getRoot();
if (Objects.equals(newParentMediaItemId, mParentMediaItemId)) {
return;
}
stop();
mParentMediaItem = parentItem;
mParentMediaItemId = newParentMediaItemId;
- mMediaBrowser.subscribe(mParentMediaItemId, mSubscriptionCallback);
+ mMediaSource.subscribeChildren(mParentMediaItemId, mSubscriptionCallback);
}
/**
@@ -332,7 +316,7 @@ public class BrowseAdapter extends RecyclerView.Adapter<BrowseViewHolder> implem
private void subscribe(MediaItemState state) {
if (!state.mIsSubscribed && state.mItem.isBrowsable()) {
- mMediaBrowser.subscribe(state.mItem.getId(), mSubscriptionCallback);
+ mMediaSource.subscribeChildren(state.mItem.getId(), mSubscriptionCallback);
state.mIsSubscribed = true;
} else {
state.mState = State.LOADED;
@@ -341,7 +325,7 @@ public class BrowseAdapter extends RecyclerView.Adapter<BrowseViewHolder> implem
private void unsubscribe(MediaItemState state) {
if (state.mIsSubscribed) {
- mMediaBrowser.unsubscribe(state.mItem.getId(), mSubscriptionCallback);
+ mMediaSource.unsubscribeChildren(state.mItem.getId(), mSubscriptionCallback);
state.mIsSubscribed = false;
}
}
@@ -370,21 +354,22 @@ public class BrowseAdapter extends RecyclerView.Adapter<BrowseViewHolder> implem
return mViewData.get(position).mViewType.ordinal();
}
- private void onItemsLoaded(String parentId, List<MediaBrowser.MediaItem> children) {
+ private void onItemsLoaded(String parentId, List<MediaItemMetadata> children) {
if (parentId.equals(mParentMediaItemId)) {
// Direct children from the requested media item id. Update subscription list.
LinkedHashMap<String, MediaItemState> newItemStates = new LinkedHashMap<>();
- for (MediaBrowser.MediaItem item : children) {
- MediaItemState itemState = mItemStates.get(item.getMediaId());
+ List<MediaItemState> itemsToSubscribe = new ArrayList<>();
+ for (MediaItemMetadata item : children) {
+ MediaItemState itemState = mItemStates.get(item.getId());
if (itemState != null) {
// Reuse existing section.
- newItemStates.put(item.getMediaId(), itemState);
- mItemStates.remove(item.getMediaId());
+ newItemStates.put(item.getId(), itemState);
+ mItemStates.remove(item.getId());
} else {
// New section, subscribe to it.
itemState = new MediaItemState(item);
- newItemStates.put(item.getMediaId(), itemState);
- subscribe(itemState);
+ newItemStates.put(item.getId(), itemState);
+ itemsToSubscribe.add(itemState);
}
}
// Remove unused sections
@@ -392,6 +377,11 @@ public class BrowseAdapter extends RecyclerView.Adapter<BrowseViewHolder> implem
unsubscribe(itemState);
}
mItemStates = newItemStates;
+ // Subscribe items once we have updated the map (updates might happen synchronously
+ // if data is already available).
+ for (MediaItemState itemState : itemsToSubscribe) {
+ subscribe(itemState);
+ }
} else {
MediaItemState itemState = mItemStates.get(parentId);
if (itemState == null) {
diff --git a/src/com/android/car/media/drawer/MediaItemsFetcher.java b/src/com/android/car/media/drawer/MediaItemsFetcher.java
index 3cef566..36c01c0 100644
--- a/src/com/android/car/media/drawer/MediaItemsFetcher.java
+++ b/src/com/android/car/media/drawer/MediaItemsFetcher.java
@@ -19,6 +19,7 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.media.MediaDescription;
import android.text.TextUtils;
+import android.view.View;
import com.android.car.apps.common.BitmapDownloader;
import com.android.car.apps.common.BitmapWorkerOptions;
@@ -114,22 +115,27 @@ interface MediaItemsFetcher {
}
Bitmap iconBitmap = description.getIconBitmap();
holder.getIcon().setImageBitmap(iconBitmap); // Ok to set null here for clearing.
- if (iconBitmap == null && description.getIconUri() != null) {
- int bitmapSize =
- context.getResources().getDimensionPixelSize(R.dimen.car_primary_icon_size);
- // We don't want to cache android resources as they are needed to be refreshed after
- // configuration changes.
- int cacheFlag = UriUtils.isAndroidResourceUri(description.getIconUri())
- ? (BitmapWorkerOptions.CACHE_FLAG_DISK_DISABLED
- | BitmapWorkerOptions.CACHE_FLAG_MEM_DISABLED)
- : 0;
- BitmapWorkerOptions options = new BitmapWorkerOptions.Builder(context)
- .resource(description.getIconUri())
- .height(bitmapSize)
- .width(bitmapSize)
- .cacheFlag(cacheFlag)
- .build();
- BitmapDownloader.getInstance(context).loadBitmap(options, holder.getIcon());
+ if (iconBitmap == null) {
+ if (description.getIconUri() != null) {
+ holder.getIcon().setVisibility(View.VISIBLE);
+ int bitmapSize =
+ context.getResources().getDimensionPixelSize(R.dimen.car_primary_icon_size);
+ // We don't want to cache android resources as they are needed to be refreshed after
+ // configuration changes.
+ int cacheFlag = UriUtils.isAndroidResourceUri(description.getIconUri())
+ ? (BitmapWorkerOptions.CACHE_FLAG_DISK_DISABLED
+ | BitmapWorkerOptions.CACHE_FLAG_MEM_DISABLED)
+ : 0;
+ BitmapWorkerOptions options = new BitmapWorkerOptions.Builder(context)
+ .resource(description.getIconUri())
+ .height(bitmapSize)
+ .width(bitmapSize)
+ .cacheFlag(cacheFlag)
+ .build();
+ BitmapDownloader.getInstance(context).loadBitmap(options, holder.getIcon());
+ } else {
+ holder.getIcon().setVisibility(View.GONE);
+ }
}
}
}
diff --git a/src/com/android/car/media/widgets/AppBarView.java b/src/com/android/car/media/widgets/AppBarView.java
index bad0641..741e07f 100644
--- a/src/com/android/car/media/widgets/AppBarView.java
+++ b/src/com/android/car/media/widgets/AppBarView.java
@@ -56,6 +56,7 @@ public class AppBarView extends RelativeLayout {
private MediaItemMetadata mSelectedItem;
private String mMediaAppTitle;
private Drawable mDefaultIcon;
+ private boolean mContentForwardEnabled;
/**
* Application bar listener
@@ -238,6 +239,13 @@ public class AppBarView extends RelativeLayout {
}
/**
+ * Whether content forward browsing is enabled or not
+ */
+ public void setContentForwardEnabled(boolean enabled) {
+ mContentForwardEnabled = enabled;
+ }
+
+ /**
* Updates the application icon to show next to the application switcher.
*/
public void setAppIcon(Bitmap icon) {
@@ -304,15 +312,18 @@ public class AppBarView extends RelativeLayout {
break;
case PLAYING:
mNavIcon.setImageDrawable(mCollapse);
- mNavIconContainer.setVisibility(hasItems ? View.GONE : View.VISIBLE);
- mTabsContainer.setVisibility(hasItems ? View.VISIBLE : View.GONE);
- mTitle.setVisibility(hasItems ? View.GONE : View.VISIBLE);
+ mNavIconContainer.setVisibility(hasItems || !mContentForwardEnabled ? View.GONE
+ : View.VISIBLE);
+ mTabsContainer.setVisibility(hasItems && mContentForwardEnabled ? View.VISIBLE
+ : View.GONE);
+ mTitle.setVisibility(hasItems || !mContentForwardEnabled ? View.GONE
+ : View.VISIBLE);
mAppSwitchIcon.setImageDrawable(mArrowDropDown);
break;
case APP_SELECTION:
mNavIconContainer.setVisibility(View.GONE);
mTabsContainer.setVisibility(View.GONE);
- mTitle.setVisibility(View.VISIBLE);
+ mTitle.setVisibility(mContentForwardEnabled ? View.VISIBLE : View.GONE);
mAppSwitchIcon.setImageDrawable(mArrowDropUp);
break;
}
diff --git a/src/com/android/car/media/widgets/ViewUtils.java b/src/com/android/car/media/widgets/ViewUtils.java
index 62e2d59..6f7ad9a 100644
--- a/src/com/android/car/media/widgets/ViewUtils.java
+++ b/src/com/android/car/media/widgets/ViewUtils.java
@@ -19,6 +19,11 @@ public class ViewUtils {
if (view.getVisibility() == View.GONE) {
return;
}
+ if (!view.isLaidOut()) {
+ // If the view hasn't been displayed yet, just adjust visibility without animation
+ view.setVisibility(View.GONE);
+ return;
+ }
view.animate()
.alpha(0f)
.setDuration(duration)
@@ -40,11 +45,20 @@ public class ViewUtils {
if (view.getVisibility() == View.VISIBLE) {
return;
}
- view.setAlpha(0f);
- view.setVisibility(View.VISIBLE);
+ if (!view.isLaidOut()) {
+ // If the view hasn't been displayed yet, just adjust visibility without animation
+ view.setVisibility(View.VISIBLE);
+ return;
+ }
view.animate()
.alpha(1f)
.setDuration(duration)
- .setListener(null);
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ view.setAlpha(0f);
+ view.setVisibility(View.VISIBLE);
+ }
+ });
}
}