Merge "Update search bar specs" into pi-car-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d354560..ff324f6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,8 +16,7 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.car.media"
-    android:sharedUserId="android.uid.system">
+    package="com.android.car.media">
 
     <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
     <uses-permission android:name="android.permission.INTERNET" />
diff --git a/res/layout/fragment_playback.xml b/res/layout/fragment_playback.xml
index 982e00a..da935d3 100644
--- a/res/layout/fragment_playback.xml
+++ b/res/layout/fragment_playback.xml
@@ -145,16 +145,22 @@
         android:layout_height="@dimen/fragment_playback_queue_overlap_bottom"
         app:layout_constraintTop_toTopOf="@+id/control_bar_first_row_guideline"/>
 
-    <com.android.car.apps.common.widget.PagedRecyclerView
-        android:id="@+id/queue_list"
+    <!-- The queue_container is a workaround for a bug in PagedRecyclerView (b/136669451). -->
+    <RelativeLayout
+        android:id="@+id/queue_container"
         android:layout_width="match_parent"
         android:layout_height="0dp"
-        android:visibility="gone"
         app:layout_constraintTop_toTopOf="@+id/queue_list_top_constraint"
-        app:layout_constraintBottom_toBottomOf="@+id/queue_list_bottom_constraint"
-        android:fadeScrollbars="true"
-        android:requiresFadingEdge="vertical"
-        android:fadingEdgeLength="@dimen/queue_fading_edge_length"/>
+        app:layout_constraintBottom_toBottomOf="@+id/queue_list_bottom_constraint">
+        <com.android.car.apps.common.widget.PagedRecyclerView
+            android:id="@+id/queue_list"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone"
+            android:fadeScrollbars="true"
+            android:requiresFadingEdge="vertical"
+            android:fadingEdgeLength="@dimen/queue_fading_edge_length"/>
+    </RelativeLayout>
 
     <include
         layout="@layout/scrim_overlay"
diff --git a/res/layout/search_bar.xml b/res/layout/search_bar.xml
index 0252a56..1c8d55f 100644
--- a/res/layout/search_bar.xml
+++ b/res/layout/search_bar.xml
@@ -50,5 +50,6 @@
         android:layout_marginStart="@dimen/appbar_view_search_margin"
         android:padding="@dimen/appbar_view_icon_padding"
         android:background="@drawable/appbar_view_icon_background"
+        android:visibility="gone"
         android:src="@drawable/ic_close"/>
 </merge>
\ No newline at end of file
diff --git a/src/com/android/car/media/BrowseFragment.java b/src/com/android/car/media/BrowseFragment.java
index 5adc766..3759ac8 100644
--- a/src/com/android/car/media/BrowseFragment.java
+++ b/src/com/android/car/media/BrowseFragment.java
@@ -271,6 +271,7 @@
             if (isLoading) {
                 ViewUtils.hideViewAnimated(mBrowseList, mFadeDuration);
                 startLoadingIndicator();
+                mBrowseAdapter.submitItems(null, null);
                 return;
             }
             stopLoadingIndicator();
diff --git a/src/com/android/car/media/MediaActivity.java b/src/com/android/car/media/MediaActivity.java
index 7c8157d..53f3814 100644
--- a/src/com/android/car/media/MediaActivity.java
+++ b/src/com/android/car/media/MediaActivity.java
@@ -242,13 +242,19 @@
                             mediaSourceViewModel.getPrimaryMediaSource().getValue());
                 });
         mediaBrowserViewModel.getBrowsedMediaItems().observe(this, futureData -> {
-            if (!futureData.isLoading()) {
-                if (futureData.getData() != null) {
-                    mIsBrowseTreeReady = true;
-                    handlePlaybackState(playbackViewModel.getPlaybackStateWrapper().getValue());
-                }
-                updateTabs(futureData.getData());
+            if (futureData.isLoading()) {
+                mIsBrowseTreeReady = false;
+                return;
             }
+            final boolean browseTreeReady =
+                    futureData.getData() != null && !futureData.getData().isEmpty();
+            if (Log.isLoggable(TAG, Log.INFO)) {
+                Log.i(TAG, "Browse tree ready status changed: " + mIsBrowseTreeReady + " -> "
+                        + browseTreeReady);
+            }
+            mIsBrowseTreeReady = browseTreeReady;
+            handlePlaybackState(playbackViewModel.getPlaybackStateWrapper().getValue(), false);
+            updateTabs(futureData.getData());
         });
         mediaBrowserViewModel.supportsSearch().observe(this,
                 mAppBarView::setSearchSupported);
@@ -286,7 +292,8 @@
                     mPlaybackController = playbackController;
                 });
 
-        playbackViewModel.getPlaybackStateWrapper().observe(this, this::handlePlaybackState);
+        playbackViewModel.getPlaybackStateWrapper().observe(this,
+                state -> handlePlaybackState(state, true));
 
         mCarUxRestrictionsUtil = CarUxRestrictionsUtil.getInstance(this);
         mRestrictions = CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP;
@@ -305,7 +312,13 @@
         return CarUxRestrictionsUtil.isRestricted(mRestrictions, mActiveCarUxRestrictions);
     }
 
-    private void handlePlaybackState(PlaybackViewModel.PlaybackStateWrapper state) {
+    private void handlePlaybackState(PlaybackViewModel.PlaybackStateWrapper state,
+            boolean ignoreSameState) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "handlePlaybackState(); state change: " + mCurrentPlaybackState + " -> "
+                    + (state != null ? state.getState() : null));
+        }
+
         // TODO(arnaudberry) rethink interactions between customized layouts and dynamic visibility.
         mCanShowMiniPlaybackControls = (state != null) && state.shouldDisplay();
 
@@ -316,13 +329,20 @@
         if (state == null) {
             return;
         }
+        if (ignoreSameState && mCurrentPlaybackState != null
+                && mCurrentPlaybackState == state.getState()) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Ignore same playback state.");
+            }
+            return;
+        }
         if (mCurrentPlaybackState == null || mCurrentPlaybackState != state.getState()) {
             mCurrentPlaybackState = state.getState();
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                Log.v(TAG, "handlePlaybackState(); state change: " + mCurrentPlaybackState);
-            }
         }
 
+        maybeCancelToast();
+        maybeCancelDialog();
+
         Bundle extras = state.getExtras();
         PendingIntent intent = extras == null ? null : extras.getParcelable(
                 MediaConstants.ERROR_RESOLUTION_ACTION_INTENT);
@@ -330,7 +350,11 @@
                 MediaConstants.ERROR_RESOLUTION_ACTION_LABEL);
         String displayedMessage = getDisplayedMessage(state);
 
+        boolean isFatalError = false;
         if (!TextUtils.isEmpty(displayedMessage)) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Error message is not empty");
+            }
             if (mIsBrowseTreeReady) {
                 if (intent != null && !isUxRestricted()) {
                     showDialog(intent, displayedMessage, label, getString(android.R.string.cancel));
@@ -340,9 +364,14 @@
             } else {
                 mErrorFragment = ErrorFragment.newInstance(displayedMessage, label, intent);
                 setErrorFragment(mErrorFragment);
-                changeMode(Mode.FATAL_ERROR);
+                isFatalError = true;
             }
         }
+        if (isFatalError) {
+            changeMode(Mode.FATAL_ERROR);
+        } else if (mMode == Mode.FATAL_ERROR) {
+            changeMode(Mode.BROWSING);
+        }
     }
 
     private String getDisplayedMessage(PlaybackViewModel.PlaybackStateWrapper state) {
@@ -363,7 +392,6 @@
 
     private void showDialog(PendingIntent intent, String message, String positiveBtnText,
             String negativeButtonText) {
-        maybeCancelDialog();
         AlertDialog.Builder dialog = new AlertDialog.Builder(this);
         mDialog = dialog.setMessage(message)
                 .setNegativeButton(negativeButtonText, null)
@@ -387,7 +415,6 @@
     }
 
     private void showToast(String message) {
-        maybeCancelToast();
         mToast = Toast.makeText(this, message, Toast.LENGTH_LONG);
         mToast.show();
     }
@@ -422,7 +449,12 @@
      * @param mediaSource the new media source we are going to try to browse
      */
     private void onMediaSourceChanged(@Nullable MediaSource mediaSource) {
+        if (Log.isLoggable(TAG, Log.INFO)) {
+            Log.i(TAG, "MediaSource changed to " + mediaSource);
+        }
+
         mIsBrowseTreeReady = false;
+        mCurrentPlaybackState = null;
         maybeCancelToast();
         maybeCancelDialog();
         if (mediaSource != null) {
diff --git a/src/com/android/car/media/widgets/SearchBar.java b/src/com/android/car/media/widgets/SearchBar.java
index 0a5e09a..ba25ca2 100644
--- a/src/com/android/car/media/widgets/SearchBar.java
+++ b/src/com/android/car/media/widgets/SearchBar.java
@@ -125,8 +125,10 @@
 
     private void onSearch(String query) {
         if (mListener == null || TextUtils.isEmpty(query)) {
+            mCloseIcon.setVisibility(GONE);
             return;
         }
+        mCloseIcon.setVisibility(VISIBLE);
         mListener.onSearch(query);
     }
 }