summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/res/layout/media_long_press_menu.xml105
-rw-r--r--packages/SystemUI/res/layout/media_session_view.xml84
-rw-r--r--packages/SystemUI/res/layout/media_smartspace_recommendations.xml94
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/GutsViewHolder.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java111
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt129
10 files changed, 382 insertions, 307 deletions
diff --git a/packages/SystemUI/res/layout/media_long_press_menu.xml b/packages/SystemUI/res/layout/media_long_press_menu.xml
new file mode 100644
index 000000000000..99c5e4707338
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_long_press_menu.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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" >
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="0dp"
+ android:layout_marginStart="@dimen/qs_media_padding"
+ android:layout_marginEnd="@dimen/qs_media_padding"
+ android:id="@+id/remove_text"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:text="@string/controls_media_close_session"
+ android:gravity="center_horizontal|top"
+ app:layout_constraintTop_toBottomOf="@id/settings"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/cancel" />
+
+ <ImageButton
+ android:id="@+id/settings"
+ android:src="@drawable/ic_settings"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:layout_marginEnd="4dp"
+ android:background="@drawable/qs_media_light_source"
+ android:contentDescription="@string/controls_media_settings_button"
+ android:layout_gravity="top"
+ app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
+ app:layout_constraintHeight_min="@dimen/min_clickable_item_size"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+ </ImageButton>
+
+ <FrameLayout
+ android:id="@+id/dismiss"
+ android:background="@drawable/qs_media_light_source"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/qs_media_padding"
+ android:layout_marginEnd="@dimen/qs_media_action_spacing"
+ android:layout_marginBottom="@dimen/qs_media_padding"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
+ app:layout_constraintHeight_min="@dimen/min_clickable_item_size"
+ app:layout_constraintHorizontal_chainStyle="packed"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/cancel"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/remove_text">
+ <TextView
+ android:id="@+id/dismiss_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center|top"
+ style="@style/MediaPlayer.SolidButton"
+ android:background="@drawable/qs_media_solid_button"
+ android:text="@string/controls_media_dismiss_button" />
+ </FrameLayout>
+ <FrameLayout
+ android:id="@+id/cancel"
+ android:background="@drawable/qs_media_light_source"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/qs_media_action_spacing"
+ android:layout_marginEnd="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_padding"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
+ app:layout_constraintHeight_min="@dimen/min_clickable_item_size"
+ app:layout_constraintStart_toEndOf="@id/dismiss"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/remove_text">
+ <TextView
+ android:id="@+id/cancel_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center|top"
+ style="@style/MediaPlayer.OutlineButton"
+ android:text="@string/cancel" />
+ </FrameLayout>
+
+</merge> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index 665edac65afc..7962e22d6b7f 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -300,87 +300,7 @@
android:layout_marginBottom="@dimen/qs_media_padding"
android:layout_marginTop="0dp" />
- <!-- Long press menu -->
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="0dp"
- android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_padding"
- android:id="@+id/remove_text"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="marquee_forever"
- android:text="@string/controls_media_close_session"
- android:gravity="center_horizontal|top"
- app:layout_constraintTop_toBottomOf="@id/settings"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toTopOf="@id/cancel" />
+ <include
+ layout="@layout/media_long_press_menu" />
- <ImageButton
- android:id="@+id/settings"
- android:src="@drawable/ic_settings"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="4dp"
- android:layout_marginEnd="4dp"
- android:background="@drawable/qs_media_light_source"
- android:contentDescription="@string/controls_media_settings_button"
- android:layout_gravity="top"
- app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
- app:layout_constraintHeight_min="@dimen/min_clickable_item_size"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent">
- </ImageButton>
-
- <FrameLayout
- android:id="@+id/dismiss"
- android:background="@drawable/qs_media_light_source"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constrainedWidth="true"
- app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
- app:layout_constraintHeight_min="@dimen/min_clickable_item_size"
- app:layout_constraintHorizontal_chainStyle="packed"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/cancel"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/remove_text">
- <TextView
- android:id="@+id/dismiss_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center|top"
- style="@style/MediaPlayer.SolidButton"
- android:background="@drawable/qs_media_solid_button"
- android:text="@string/controls_media_dismiss_button" />
- </FrameLayout>
- <FrameLayout
- android:id="@+id/cancel"
- android:background="@drawable/qs_media_light_source"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_padding"
- android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constrainedWidth="true"
- app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
- app:layout_constraintHeight_min="@dimen/min_clickable_item_size"
- app:layout_constraintStart_toEndOf="@id/dismiss"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/remove_text">
- <TextView
- android:id="@+id/cancel_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center|top"
- style="@style/MediaPlayer.OutlineButton"
- android:text="@string/cancel" />
- </FrameLayout>
</com.android.systemui.util.animation.TransitionLayout>
diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
index 5510f24870bb..659a578aa61f 100644
--- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
+++ b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
@@ -167,97 +167,7 @@
android:layout_marginBottom="@dimen/qs_media_padding"
/>
- <!-- Long press menu -->
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/qs_media_padding"
- android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_padding"
- android:id="@+id/remove_text"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="marquee_forever"
- android:text="@string/controls_media_close_session"
- android:gravity="center_horizontal|top"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toTopOf="@id/cancel"/>
-
- <FrameLayout
- android:id="@+id/settings"
- android:background="@drawable/qs_media_light_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constrainedWidth="true"
- app:layout_constraintWidth_min="48dp"
- app:layout_constraintHeight_min="48dp"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/cancel"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/remove_text">
-
- <TextView
- android:id="@+id/settings_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center|bottom"
- style="@style/MediaPlayer.OutlineButton"
- android:text="@string/controls_media_settings_button" />
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/cancel"
- android:background="@drawable/qs_media_light_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constrainedWidth="true"
- app:layout_constraintWidth_min="48dp"
- app:layout_constraintHeight_min="48dp"
- app:layout_constraintStart_toEndOf="@id/settings"
- app:layout_constraintEnd_toStartOf="@id/dismiss"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/remove_text">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center|bottom"
- style="@style/MediaPlayer.OutlineButton"
- android:text="@string/cancel" />
- </FrameLayout>
+ <include
+ layout="@layout/media_long_press_menu" />
- <FrameLayout
- android:id="@+id/dismiss"
- android:background="@drawable/qs_media_light_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_padding"
- android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constrainedWidth="true"
- app:layout_constraintWidth_min="48dp"
- app:layout_constraintHeight_min="48dp"
- app:layout_constraintStart_toEndOf="@id/cancel"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/remove_text">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center|bottom"
- style="@style/MediaPlayer.OutlineButton"
- android:text="@string/controls_media_dismiss_button"
- />
- </FrameLayout>
</com.android.systemui.util.animation.TransitionLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
index 5023cf685457..5a214d1cd5e0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
@@ -103,7 +103,7 @@ class ColorSchemeTransition internal constructor(
mediaViewHolder.albumView.backgroundTintList = colorList
mediaViewHolder.seamlessIcon.imageTintList = colorList
mediaViewHolder.seamlessText.setTextColor(surfaceColor)
- mediaViewHolder.dismissText.setTextColor(surfaceColor)
+ mediaViewHolder.gutsViewHolder.setSurfaceColor(surfaceColor)
}
val accentPrimary = colorTransitionFactory(
@@ -113,9 +113,7 @@ class ColorSchemeTransition internal constructor(
val accentColorList = ColorStateList.valueOf(accentPrimary)
mediaViewHolder.actionPlayPause.backgroundTintList = accentColorList
mediaViewHolder.seamlessButton.backgroundTintList = accentColorList
- mediaViewHolder.settings.imageTintList = accentColorList
- mediaViewHolder.cancelText.backgroundTintList = accentColorList
- mediaViewHolder.dismissText.backgroundTintList = accentColorList
+ mediaViewHolder.gutsViewHolder.setAccentPrimaryColor(accentPrimary)
}
val textPrimary = colorTransitionFactory(
@@ -126,13 +124,12 @@ class ColorSchemeTransition internal constructor(
val textColorList = ColorStateList.valueOf(textPrimary)
mediaViewHolder.seekBar.thumb.setTintList(textColorList)
mediaViewHolder.seekBar.progressTintList = textColorList
- mediaViewHolder.longPressText.setTextColor(textColorList)
- mediaViewHolder.cancelText.setTextColor(textColorList)
mediaViewHolder.scrubbingElapsedTimeView.setTextColor(textColorList)
mediaViewHolder.scrubbingTotalTimeView.setTextColor(textColorList)
for (button in mediaViewHolder.getTransparentActionButtons()) {
button.imageTintList = textColorList
}
+ mediaViewHolder.gutsViewHolder.setTextPrimaryColor(textPrimary)
}
val textPrimaryInverse = colorTransitionFactory(
diff --git a/packages/SystemUI/src/com/android/systemui/media/GutsViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/GutsViewHolder.kt
new file mode 100644
index 000000000000..1a48a84aff23
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/GutsViewHolder.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.systemui.media
+
+import android.content.res.ColorStateList
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageButton
+import android.widget.TextView
+import com.android.systemui.R
+import com.android.systemui.monet.ColorScheme
+
+/**
+ * A view holder for the guts menu of a media player. The guts are shown when the user long-presses
+ * on the media player.
+ *
+ * Both [MediaViewHolder] and [RecommendationViewHolder] use the same guts menu layout, so this
+ * class helps share logic between the two.
+ */
+class GutsViewHolder constructor(itemView: View) {
+ val gutsText: TextView = itemView.requireViewById(R.id.remove_text)
+ val cancel: View = itemView.requireViewById(R.id.cancel)
+ val cancelText: TextView = itemView.requireViewById(R.id.cancel_text)
+ val dismiss: ViewGroup = itemView.requireViewById(R.id.dismiss)
+ val dismissText: TextView = itemView.requireViewById(R.id.dismiss_text)
+ val settings: ImageButton = itemView.requireViewById(R.id.settings)
+
+ /** Marquees the main text of the guts menu. */
+ fun marquee(start: Boolean, delay: Long, tag: String) {
+ val gutsTextHandler = gutsText.handler
+ if (gutsTextHandler == null) {
+ Log.d(tag, "marquee while longPressText.getHandler() is null", Exception())
+ return
+ }
+ gutsTextHandler.postDelayed( { gutsText.isSelected = start }, delay)
+ }
+
+ /** Sets the right colors on all the guts views based on the given [ColorScheme]. */
+ fun setColors(colorScheme: ColorScheme) {
+ setSurfaceColor(surfaceFromScheme(colorScheme))
+ setTextPrimaryColor(textPrimaryFromScheme(colorScheme))
+ setAccentPrimaryColor(accentPrimaryFromScheme(colorScheme))
+ }
+
+ /** Sets the surface color on all guts views that use it. */
+ fun setSurfaceColor(surfaceColor: Int) {
+ dismissText.setTextColor(surfaceColor)
+ }
+
+ /** Sets the primary accent color on all guts views that use it. */
+ fun setAccentPrimaryColor(accentPrimary: Int) {
+ val accentColorList = ColorStateList.valueOf(accentPrimary)
+ settings.imageTintList = accentColorList
+ cancelText.backgroundTintList = accentColorList
+ dismissText.backgroundTintList = accentColorList
+ }
+
+ /** Sets the primary text color on all guts views that use it. */
+ fun setTextPrimaryColor(textPrimary: Int) {
+ val textColorList = ColorStateList.valueOf(textPrimary)
+ gutsText.setTextColor(textColorList)
+ cancelText.setTextColor(textColorList)
+ }
+
+ companion object {
+ val ids = setOf(
+ R.id.remove_text,
+ R.id.cancel,
+ R.id.dismiss,
+ R.id.settings
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 3faccdb6ffda..af54e966ed9c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -336,17 +336,6 @@ public class MediaControlPanel {
return true;
}
});
- vh.getCancel().setOnClickListener(v -> {
- if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- closeGuts();
- }
- });
- vh.getSettings().setOnClickListener(v -> {
- if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- mLogger.logLongPressSettings(mUid, mPackageName, mInstanceId);
- mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */);
- }
- });
TextView titleText = mMediaViewHolder.getTitleText();
TextView artistText = mMediaViewHolder.getArtistText();
@@ -391,17 +380,6 @@ public class MediaControlPanel {
return true;
}
});
- mRecommendationViewHolder.getCancel().setOnClickListener(v -> {
- if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- closeGuts();
- }
- });
- mRecommendationViewHolder.getSettings().setOnClickListener(v -> {
- if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- mLogger.logLongPressSettings(mUid, mPackageName, mInstanceId);
- mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */);
- }
- });
}
/** Bind this player view based on the data given. */
@@ -461,7 +439,7 @@ public class MediaControlPanel {
mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller));
bindOutputSwitcherChip(data);
- bindLongPressMenu(data);
+ bindGutsMenuForPlayer(data);
bindScrubbingTime(data);
bindActionButtons(data);
@@ -531,24 +509,8 @@ public class MediaControlPanel {
});
}
- private void bindLongPressMenu(MediaData data) {
- boolean isDismissible = data.isClearable();
- String dismissText;
- if (isDismissible) {
- dismissText = mContext.getString(R.string.controls_media_close_session, data.getApp());
- } else {
- dismissText = mContext.getString(R.string.controls_media_active_session);
- }
- mMediaViewHolder.getLongPressText().setText(dismissText);
-
- // Dismiss button
- mMediaViewHolder.getDismissText().setAlpha(isDismissible ? 1 : DISABLED_ALPHA);
- mMediaViewHolder.getDismiss().setEnabled(isDismissible);
- mMediaViewHolder.getDismiss().setOnClickListener(v -> {
- if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return;
- logSmartspaceCardReported(SMARTSPACE_CARD_DISMISS_EVENT);
- mLogger.logLongPressDismiss(mUid, mPackageName, mInstanceId);
-
+ private void bindGutsMenuForPlayer(MediaData data) {
+ Runnable onDismissClickedRunnable = () -> {
if (mKey != null) {
closeGuts();
if (!mMediaDataManagerLazy.get().dismissMediaData(mKey,
@@ -561,7 +523,13 @@ public class MediaControlPanel {
Log.w(TAG, "Dismiss media with null notification. Token uid="
+ data.getToken().getUid());
}
- });
+ };
+
+ bindGutsMenuCommon(
+ /* isDismissible= */ data.isClearable(),
+ data.getApp(),
+ mMediaViewHolder.getGutsViewHolder(),
+ onDismissClickedRunnable);
}
private boolean bindSongMetadata(MediaData data) {
@@ -1087,14 +1055,9 @@ public class MediaControlPanel {
}
mSmartspaceMediaItemsCount = uiComponentIndex;
- // Set up long press to show guts setting panel.
- mRecommendationViewHolder.getDismiss().setOnClickListener(v -> {
- if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return;
- mLogger.logLongPressDismiss(mUid, mPackageName, mInstanceId);
- logSmartspaceCardReported(
- 761 // SMARTSPACE_CARD_DISMISS
- );
+ // Guts
+ Runnable onDismissClickedRunnable = () -> {
closeGuts();
mMediaDataManagerLazy.get().dismissSmartspaceRecommendation(
data.getTargetId(), MediaViewController.GUTS_ANIMATION_DURATION + 100L);
@@ -1114,7 +1077,12 @@ public class MediaControlPanel {
} else {
mBroadcastSender.sendBroadcast(dismissIntent);
}
- });
+ };
+ bindGutsMenuCommon(
+ /* isDismissible= */ true,
+ appName.toString(),
+ mRecommendationViewHolder.getGutsViewHolder(),
+ onDismissClickedRunnable);
mController = null;
if (mMetadataAnimationHandler == null || !mMetadataAnimationHandler.isRunning()) {
@@ -1145,6 +1113,49 @@ public class MediaControlPanel {
(title) -> title.setTextColor(textPrimaryColor));
mRecommendationViewHolder.getMediaSubtitles().forEach(
(subtitle) -> subtitle.setTextColor(textSecondaryColor));
+
+ mRecommendationViewHolder.getGutsViewHolder().setColors(colorScheme);
+ }
+
+ private void bindGutsMenuCommon(
+ boolean isDismissible,
+ String appName,
+ GutsViewHolder gutsViewHolder,
+ Runnable onDismissClickedRunnable) {
+ // Text
+ String text;
+ if (isDismissible) {
+ text = mContext.getString(R.string.controls_media_close_session, appName);
+ } else {
+ text = mContext.getString(R.string.controls_media_active_session);
+ }
+ gutsViewHolder.getGutsText().setText(text);
+
+ // Dismiss button
+ gutsViewHolder.getDismissText().setAlpha(isDismissible ? 1 : DISABLED_ALPHA);
+ gutsViewHolder.getDismiss().setEnabled(isDismissible);
+ gutsViewHolder.getDismiss().setOnClickListener(v -> {
+ if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return;
+ logSmartspaceCardReported(SMARTSPACE_CARD_DISMISS_EVENT);
+ mLogger.logLongPressDismiss(mUid, mPackageName, mInstanceId);
+
+ onDismissClickedRunnable.run();
+ });
+
+ // Cancel button
+ gutsViewHolder.getCancel().setOnClickListener(v -> {
+ if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ closeGuts();
+ }
+ });
+
+ // Settings button
+ gutsViewHolder.getSettings().setOnClickListener(v -> {
+ if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ mLogger.logLongPressSettings(mUid, mPackageName, mInstanceId);
+ mActivityStarter.startActivity(SETTINGS_INTENT, /* dismissShade= */true);
+ }
+ });
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index 327268bcbd47..1437c965512e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -261,10 +261,7 @@ class MediaViewController @Inject constructor(
TYPE.PLAYER -> MediaViewHolder.controlsIds
TYPE.RECOMMENDATION -> RecommendationViewHolder.controlsIds
}
- val gutsIds = when (type) {
- TYPE.PLAYER -> MediaViewHolder.gutsIds
- TYPE.RECOMMENDATION -> RecommendationViewHolder.gutsIds
- }
+ val gutsIds = GutsViewHolder.ids
controlsIds.forEach { id ->
viewState.widgetStates.get(id)?.let { state ->
// Make sure to use the unmodified state if guts are not visible.
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
index b8b731868e5c..5c93cdaeb0da 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
@@ -16,7 +16,6 @@
package com.android.systemui.media
-import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -56,13 +55,7 @@ class MediaViewHolder constructor(itemView: View) {
val scrubbingTotalTimeView: TextView =
itemView.requireViewById(R.id.media_scrubbing_total_time)
- // Settings screen
- val longPressText = itemView.requireViewById<TextView>(R.id.remove_text)
- val cancel = itemView.requireViewById<View>(R.id.cancel)
- val cancelText = itemView.requireViewById<TextView>(R.id.cancel_text)
- val dismiss = itemView.requireViewById<ViewGroup>(R.id.dismiss)
- val dismissText = itemView.requireViewById<TextView>(R.id.dismiss_text)
- val settings = itemView.requireViewById<ImageButton>(R.id.settings)
+ val gutsViewHolder = GutsViewHolder(itemView)
// Action Buttons
val actionPlayPause = itemView.requireViewById<ImageButton>(R.id.actionPlayPause)
@@ -79,9 +72,9 @@ class MediaViewHolder constructor(itemView: View) {
init {
(player.background as IlluminationDrawable).let {
it.registerLightSource(seamless)
- it.registerLightSource(cancel)
- it.registerLightSource(dismiss)
- it.registerLightSource(settings)
+ it.registerLightSource(gutsViewHolder.cancel)
+ it.registerLightSource(gutsViewHolder.dismiss)
+ it.registerLightSource(gutsViewHolder.settings)
it.registerLightSource(actionPlayPause)
it.registerLightSource(actionNext)
it.registerLightSource(actionPrev)
@@ -122,12 +115,7 @@ class MediaViewHolder constructor(itemView: View) {
}
fun marquee(start: Boolean, delay: Long) {
- val longPressTextHandler = longPressText.getHandler()
- if (longPressTextHandler == null) {
- Log.d(TAG, "marquee while longPressText.getHandler() is null", Exception())
- return
- }
- longPressTextHandler.postDelayed({ longPressText.setSelected(start) }, delay)
+ gutsViewHolder.marquee(start, delay, TAG)
}
companion object {
@@ -172,12 +160,7 @@ class MediaViewHolder constructor(itemView: View) {
R.id.media_scrubbing_elapsed_time,
R.id.media_scrubbing_total_time
)
- val gutsIds = setOf(
- R.id.remove_text,
- R.id.cancel,
- R.id.dismiss,
- R.id.settings
- )
+
// Buttons used for notification-based actions
val genericButtonIds = setOf(
diff --git a/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
index a83984036f60..52ac4e0682a3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
@@ -24,6 +24,8 @@ import android.widget.TextView
import com.android.systemui.R
import com.android.systemui.util.animation.TransitionLayout
+private const val TAG = "RecommendationViewHolder"
+
/** ViewHolder for a Smartspace media recommendation. */
class RecommendationViewHolder private constructor(itemView: View) {
@@ -52,26 +54,19 @@ class RecommendationViewHolder private constructor(itemView: View) {
itemView.requireViewById(R.id.media_subtitle3)
)
- // Settings/Guts screen
- val longPressText = itemView.requireViewById<TextView>(R.id.remove_text)
- val cancel = itemView.requireViewById<View>(R.id.cancel)
- val dismiss = itemView.requireViewById<ViewGroup>(R.id.dismiss)
- val dismissLabel = dismiss.getChildAt(0)
- val settings = itemView.requireViewById<View>(R.id.settings)
- val settingsText = itemView.requireViewById<TextView>(R.id.settings_text)
+ val gutsViewHolder = GutsViewHolder(itemView)
init {
(recommendations.background as IlluminationDrawable).let { background ->
mediaCoverContainers.forEach { background.registerLightSource(it) }
- background.registerLightSource(cancel)
- background.registerLightSource(dismiss)
- background.registerLightSource(dismissLabel)
- background.registerLightSource(settings)
+ background.registerLightSource(gutsViewHolder.cancel)
+ background.registerLightSource(gutsViewHolder.dismiss)
+ background.registerLightSource(gutsViewHolder.settings)
}
}
fun marquee(start: Boolean, delay: Long) {
- longPressText.getHandler().postDelayed({ longPressText.setSelected(start) }, delay)
+ gutsViewHolder.marquee(start, delay, TAG)
}
companion object {
@@ -104,14 +99,12 @@ class RecommendationViewHolder private constructor(itemView: View) {
R.id.media_cover1_container,
R.id.media_cover2_container,
R.id.media_cover3_container,
- )
-
- // Res Ids for the components on the guts panel.
- val gutsIds = setOf(
- R.id.remove_text,
- R.id.cancel,
- R.id.dismiss,
- R.id.settings
+ R.id.media_title1,
+ R.id.media_title2,
+ R.id.media_title3,
+ R.id.media_subtitle1,
+ R.id.media_subtitle2,
+ R.id.media_subtitle3
)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 4e130d10f6ec..83fb82c1c493 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -25,7 +25,6 @@ import org.mockito.Mockito.`when` as whenever
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
-import android.graphics.Color
import android.graphics.drawable.Animatable2
import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.GradientDrawable
@@ -35,7 +34,6 @@ import android.media.MediaMetadata
import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.os.Bundle
-import android.os.Handler
import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -111,6 +109,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var broadcastSender: BroadcastSender
+ @Mock private lateinit var gutsViewHolder: GutsViewHolder
@Mock private lateinit var viewHolder: MediaViewHolder
@Mock private lateinit var view: TransitionLayout
@Mock private lateinit var seekBarViewModel: SeekBarViewModel
@@ -145,8 +144,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
private lateinit var scrubbingElapsedTimeView: TextView
private lateinit var scrubbingTotalTimeView: TextView
private lateinit var actionsTopBarrier: Barrier
- @Mock private lateinit var longPressText: TextView
- @Mock private lateinit var handler: Handler
+ @Mock private lateinit var gutsText: TextView
@Mock private lateinit var mockAnimator: AnimatorSet
private lateinit var settings: ImageButton
private lateinit var cancel: View
@@ -228,6 +226,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
}
+ initGutsViewHolderMocks()
initMediaViewHolderMocks()
// Create media session
@@ -272,6 +271,20 @@ public class MediaControlPanelTest : SysuiTestCase() {
)
}
+ private fun initGutsViewHolderMocks() {
+ settings = ImageButton(context)
+ cancel = View(context)
+ cancelText = TextView(context)
+ dismiss = FrameLayout(context)
+ dismissText = TextView(context)
+ whenever(gutsViewHolder.gutsText).thenReturn(gutsText)
+ whenever(gutsViewHolder.settings).thenReturn(settings)
+ whenever(gutsViewHolder.cancel).thenReturn(cancel)
+ whenever(gutsViewHolder.cancelText).thenReturn(cancelText)
+ whenever(gutsViewHolder.dismiss).thenReturn(dismiss)
+ whenever(gutsViewHolder.dismissText).thenReturn(dismissText)
+ }
+
/**
* Initialize elements in media view holder
*/
@@ -292,11 +305,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
seamlessIcon = ImageView(context)
seamlessText = TextView(context)
seekBar = SeekBar(context).also { it.id = R.id.media_progress_bar }
- settings = ImageButton(context)
- cancel = View(context)
- cancelText = TextView(context)
- dismiss = FrameLayout(context)
- dismissText = TextView(context)
action0 = ImageButton(context).also { it.setId(R.id.action0) }
action1 = ImageButton(context).also { it.setId(R.id.action1) }
@@ -341,6 +349,8 @@ public class MediaControlPanelTest : SysuiTestCase() {
whenever(viewHolder.scrubbingElapsedTimeView).thenReturn(scrubbingElapsedTimeView)
whenever(viewHolder.scrubbingTotalTimeView).thenReturn(scrubbingTotalTimeView)
+ whenever(viewHolder.gutsViewHolder).thenReturn(gutsViewHolder)
+
// Transition View
whenever(view.parent).thenReturn(transitionParent)
whenever(view.rootView).thenReturn(transitionParent)
@@ -363,15 +373,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
whenever(viewHolder.action4).thenReturn(action4)
whenever(viewHolder.getAction(R.id.action4)).thenReturn(action4)
- // Long press menu
- whenever(viewHolder.longPressText).thenReturn(longPressText)
- whenever(longPressText.handler).thenReturn(handler)
- whenever(viewHolder.settings).thenReturn(settings)
- whenever(viewHolder.cancel).thenReturn(cancel)
- whenever(viewHolder.cancelText).thenReturn(cancelText)
- whenever(viewHolder.dismiss).thenReturn(dismiss)
- whenever(viewHolder.dismissText).thenReturn(dismissText)
-
whenever(viewHolder.actionsTopBarrier).thenReturn(actionsTopBarrier)
}
@@ -404,10 +405,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
listOf(recSubtitle1, recSubtitle2, recSubtitle3)
)
- // Long press menu
- whenever(recommendationViewHolder.settings).thenReturn(settings)
- whenever(recommendationViewHolder.cancel).thenReturn(cancel)
- whenever(recommendationViewHolder.dismiss).thenReturn(dismiss)
+ whenever(recommendationViewHolder.gutsViewHolder).thenReturn(gutsViewHolder)
val actionIcon = Icon.createWithResource(context, R.drawable.ic_android)
whenever(smartspaceAction.icon).thenReturn(actionIcon)
@@ -968,8 +966,10 @@ public class MediaControlPanelTest : SysuiTestCase() {
assertThat(seamless.isEnabled()).isFalse()
}
+ /* ***** Guts tests for the player ***** */
+
@Test
- fun longClick_gutsClosed() {
+ fun player_longClickWhenGutsClosed_gutsOpens() {
player.attachPlayer(viewHolder)
player.bindPlayer(mediaData, KEY)
whenever(mediaViewController.isGutsVisible).thenReturn(false)
@@ -983,7 +983,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun longClick_gutsOpen() {
+ fun player_longClickWhenGutsOpen_gutsCloses() {
player.attachPlayer(viewHolder)
whenever(mediaViewController.isGutsVisible).thenReturn(true)
@@ -996,8 +996,9 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun cancelButtonClick_animation() {
+ fun player_cancelButtonClick_animation() {
player.attachPlayer(viewHolder)
+ player.bindPlayer(mediaData, KEY)
cancel.callOnClick()
@@ -1005,7 +1006,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun settingsButtonClick() {
+ fun player_settingsButtonClick() {
player.attachPlayer(viewHolder)
player.bindPlayer(mediaData, KEY)
@@ -1019,7 +1020,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun dismissButtonClick() {
+ fun player_dismissButtonClick() {
val mediaKey = "key for dismissal"
player.attachPlayer(viewHolder)
val state = mediaData.copy(notificationKey = KEY)
@@ -1032,7 +1033,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun dismissButtonDisabled() {
+ fun player_dismissButtonDisabled() {
val mediaKey = "key for dismissal"
player.attachPlayer(viewHolder)
val state = mediaData.copy(isClearable = false, notificationKey = KEY)
@@ -1042,7 +1043,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun dismissButtonClick_notInManager() {
+ fun player_dismissButtonClick_notInManager() {
val mediaKey = "key for dismissal"
whenever(mediaDataManager.dismissMediaData(eq(mediaKey), anyLong())).thenReturn(false)
@@ -1057,6 +1058,76 @@ public class MediaControlPanelTest : SysuiTestCase() {
verify(mediaCarouselController).removePlayer(eq(mediaKey), eq(false), eq(false))
}
+ /* ***** END guts tests for the player ***** */
+
+ /* ***** Guts tests for the recommendations ***** */
+
+ @Test
+ fun recommendations_longClickWhenGutsClosed_gutsOpens() {
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+ whenever(mediaViewController.isGutsVisible).thenReturn(false)
+
+ val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+ verify(viewHolder.player).onLongClickListener = captor.capture()
+
+ captor.value.onLongClick(viewHolder.player)
+ verify(mediaViewController).openGuts()
+ verify(logger).logLongPressOpen(anyInt(), eq(PACKAGE), eq(instanceId))
+ }
+
+ @Test
+ fun recommendations_longClickWhenGutsOpen_gutsCloses() {
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+ whenever(mediaViewController.isGutsVisible).thenReturn(true)
+
+ val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+ verify(viewHolder.player).onLongClickListener = captor.capture()
+
+ captor.value.onLongClick(viewHolder.player)
+ verify(mediaViewController, never()).openGuts()
+ verify(mediaViewController).closeGuts(false)
+ }
+
+ @Test
+ fun recommendations_cancelButtonClick_animation() {
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+
+ cancel.callOnClick()
+
+ verify(mediaViewController).closeGuts(false)
+ }
+
+ @Test
+ fun recommendations_settingsButtonClick() {
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+
+ settings.callOnClick()
+ verify(logger).logLongPressSettings(anyInt(), eq(PACKAGE), eq(instanceId))
+
+ val captor = ArgumentCaptor.forClass(Intent::class.java)
+ verify(activityStarter).startActivity(captor.capture(), eq(true))
+
+ assertThat(captor.value.action).isEqualTo(ACTION_MEDIA_CONTROLS_SETTINGS)
+ }
+
+ @Test
+ fun recommendations_dismissButtonClick() {
+ val mediaKey = "key for dismissal"
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData.copy(targetId = mediaKey))
+
+ assertThat(dismiss.isEnabled).isEqualTo(true)
+ dismiss.callOnClick()
+ verify(logger).logLongPressDismiss(anyInt(), eq(PACKAGE), eq(instanceId))
+ verify(mediaDataManager).dismissSmartspaceRecommendation(eq(mediaKey), anyLong())
+ }
+
+ /* ***** END guts tests for the recommendations ***** */
+
@Test
fun actionPlayPauseClick_isLogged() {
val semanticActions = MediaButton(