Merge "Add the guts control panel to Smartspace media recommendation card." into sc-dev

This commit is contained in:
Cecilia Hong
2021-04-26 21:11:21 +00:00
committed by Android (Google) Code Review
7 changed files with 193 additions and 34 deletions

View File

@@ -18,6 +18,7 @@
<!-- Layout for media recommendations inside QSPanel carousel -->
<com.android.systemui.util.animation.TransitionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/media_recommendations"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -84,4 +85,99 @@
android:layout_width="@dimen/qs_aa_media_rec_icon_size"
android:layout_height="@dimen/qs_aa_media_rec_icon_size" />
<!-- Constraints are set here as they are the same regardless of host -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
android:id="@+id/recommendation_text"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textColor="?android:attr/textColorSecondary"
android:text="@string/controls_media_title"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@id/remove_text"
app:layout_constraintVertical_chainStyle="spread_inside"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
android:id="@+id/remove_text"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/controls_media_close_session"
app:layout_constraintTop_toBottomOf="@id/recommendation_text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@id/settings"/>
<FrameLayout
android:id="@+id/settings"
android:background="@drawable/qs_media_light_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
android:paddingBottom="@dimen/qs_media_panel_outer_padding"
android:minWidth="48dp"
android:minHeight="48dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/remove_text">
<TextView
android:layout_gravity="bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/controls_media_settings_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_marginEnd="@dimen/qs_media_panel_outer_padding"
android:paddingBottom="@dimen/qs_media_panel_outer_padding"
android:minWidth="48dp"
android:minHeight="48dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/dismiss" >
<TextView
android:layout_gravity="bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/cancel" />
</FrameLayout>
<FrameLayout
android:id="@+id/dismiss"
android:background="@drawable/qs_media_light_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
android:paddingBottom="@dimen/qs_media_panel_outer_padding"
android:minWidth="48dp"
android:minHeight="48dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<TextView
android:layout_gravity="bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/controls_media_dismiss_button"
/>
</FrameLayout>
</com.android.systemui.util.animation.TransitionLayout>

View File

@@ -319,7 +319,7 @@ class MediaCarouselController @Inject constructor(
val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
newRecs.recommendationViewHolder?.recommendations?.setLayoutParams(lp)
newRecs.bindRecommendation(data, bgColor, { v -> removePlayer(key) })
newRecs.bindRecommendation(data, bgColor)
MediaPlayerData.addMediaPlayer(key, newRecs)
updatePlayerToState(newRecs, noAnimation = true)
reorderAllPlayers()
@@ -348,11 +348,11 @@ class MediaCarouselController @Inject constructor(
if (dismissMediaData) {
// Inform the media manager of a potentially late dismissal
mediaManager.dismissMediaData(key, 0L)
mediaManager.dismissMediaData(key, 0L /* delaye */)
}
if (dismissRecommendation) {
// Inform the media manager of a potentially late dismissal
mediaManager.dismissSmartspaceRecommendation()
mediaManager.dismissSmartspaceRecommendation(0L /* delay */)
}
}
}

View File

@@ -242,6 +242,21 @@ public class MediaControlPanel {
TransitionLayout recommendations = vh.getRecommendations();
mMediaViewController.attach(recommendations, MediaViewController.TYPE.RECOMMENDATION);
mRecommendationViewHolder.getRecommendations().setOnLongClickListener(v -> {
if (!mMediaViewController.isGutsVisible()) {
mMediaViewController.openGuts();
return true;
} else {
return false;
}
});
mRecommendationViewHolder.getCancel().setOnClickListener(v -> {
closeGuts();
});
mRecommendationViewHolder.getSettings().setOnClickListener(v -> {
mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */);
});
}
/** Bind this player view based on the data given. */
@@ -461,10 +476,7 @@ public class MediaControlPanel {
}
/** Bind this recommendation view based on the data given. */
public void bindRecommendation(
@NonNull SmartspaceTarget target,
@NonNull int backgroundColor,
@Nullable View.OnClickListener callback) {
public void bindRecommendation(@NonNull SmartspaceTarget target, @NonNull int backgroundColor) {
if (mRecommendationViewHolder == null) {
return;
}
@@ -520,8 +532,10 @@ public class MediaControlPanel {
mediaCoverImageView.setImageIcon(recommendation.getIcon());
// Set up the click listener if applicable.
setSmartspaceOnClickListener(mediaCoverImageView, recommendation,
target.getSmartspaceTargetId(), callback);
setSmartspaceRecItemOnClickListener(mediaCoverImageView, recommendation,
view -> mMediaDataManagerLazy
.get()
.dismissSmartspaceRecommendation(0L /* delay */));
setVisibleAndAlpha(expandedSet, mediaCoverItemsResIds.get(i), true);
setVisibleAndAlpha(expandedSet, mediaLogoItemsResIds.get(i), true);
@@ -529,6 +543,16 @@ public class MediaControlPanel {
setVisibleAndAlpha(collapsedSet, mediaLogoItemsResIds.get(i), true);
}
// Set up long press to show guts setting panel.
mRecommendationViewHolder.getDismiss().setOnClickListener(v -> {
closeGuts();
mKeyguardDismissUtil.executeWhenUnlocked(() -> {
mMediaDataManagerLazy.get().dismissSmartspaceRecommendation(
MediaViewController.GUTS_ANIMATION_DURATION + 100L);
return true;
}, true /* requiresShadeOpen */);
});
mController = null;
mMediaViewController.refreshState();
}
@@ -613,10 +637,9 @@ public class MediaControlPanel {
set.setAlpha(actionId, visible ? 1.0f : 0.0f);
}
private void setSmartspaceOnClickListener(
private void setSmartspaceRecItemOnClickListener(
@NonNull View view,
@NonNull SmartspaceAction action,
@NonNull String targetId,
@Nullable View.OnClickListener callback) {
if (view == null || action == null || action.getIntent() == null) {
Log.e(TAG, "No tap action can be set up");
@@ -624,16 +647,6 @@ public class MediaControlPanel {
}
view.setOnClickListener(v -> {
// When media recommendation card is shown, there could be only one card.
SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
760, // SMARTSPACE_CARD_CLICK
targetId.hashCode(),
SysUiStatsLog
.SMART_SPACE_CARD_REPORTED__CARD_TYPE__HEADPHONE_MEDIA_RECOMMENDATIONS,
getSurfaceForSmartspaceLogging(mMediaViewController.getCurrentEndLocation()),
/* rank */ 1,
/* cardinality */ 1);
mActivityStarter.postStartActivityDismissingKeyguard(
action.getIntent(),
0 /* delay */,

View File

@@ -191,7 +191,7 @@ class MediaDataFilter @Inject constructor(
mediaDataManager.setTimedOut(it, timedOut = true, forceUpdate = true)
}
if (hasSmartspace) {
mediaDataManager.dismissSmartspaceRecommendation()
mediaDataManager.dismissSmartspaceRecommendation(0L /* delay */)
}
}

View File

@@ -429,12 +429,13 @@ class MediaDataManager(
* This will make the recommendation view to not be shown anymore during this headphone
* connection session.
*/
fun dismissSmartspaceRecommendation() {
fun dismissSmartspaceRecommendation(delay: Long) {
Log.d(TAG, "Dismissing Smartspace media target")
// Do not set smartspaceMediaTarget to null. So the instance is preserved during the entire
// headphone connection, and will ONLY be set to null when headphones are disconnected.
smartspaceMediaTarget?.let {
notifySmartspaceMediaDataRemoved(it.smartspaceTargetId)
foregroundExecutor.executeDelayed(
{ notifySmartspaceMediaDataRemoved(it.smartspaceTargetId) }, delay)
}
}

View File

@@ -252,16 +252,30 @@ class MediaViewController @Inject constructor(
* [TransitionViewState].
*/
private fun setGutsViewState(viewState: TransitionViewState) {
PlayerViewHolder.controlsIds.forEach { id ->
viewState.widgetStates.get(id)?.let { state ->
// Make sure to use the unmodified state if guts are not visible
state.alpha = if (isGutsVisible) 0f else state.alpha
state.gone = if (isGutsVisible) true else state.gone
if (type == TYPE.PLAYER) {
PlayerViewHolder.controlsIds.forEach { id ->
viewState.widgetStates.get(id)?.let { state ->
// Make sure to use the unmodified state if guts are not visible.
state.alpha = if (isGutsVisible) 0f else state.alpha
state.gone = if (isGutsVisible) true else state.gone
}
}
PlayerViewHolder.gutsIds.forEach { id ->
viewState.widgetStates.get(id)?.alpha = if (isGutsVisible) 1f else 0f
viewState.widgetStates.get(id)?.gone = !isGutsVisible
}
} else {
RecommendationViewHolder.controlsIds.forEach { id ->
viewState.widgetStates.get(id)?.let { state ->
// Make sure to use the unmodified state if guts are not visible.
state.alpha = if (isGutsVisible) 0f else state.alpha
state.gone = if (isGutsVisible) true else state.gone
}
}
RecommendationViewHolder.gutsIds.forEach { id ->
viewState.widgetStates.get(id)?.alpha = if (isGutsVisible) 1f else 0f
viewState.widgetStates.get(id)?.gone = !isGutsVisible
}
}
PlayerViewHolder.gutsIds.forEach { id ->
viewState.widgetStates.get(id)?.alpha = if (isGutsVisible) 1f else 0f
viewState.widgetStates.get(id)?.gone = !isGutsVisible
}
}

View File

@@ -20,6 +20,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.IntegerRes
import com.android.systemui.R
import com.android.systemui.util.animation.TransitionLayout
@@ -28,6 +29,8 @@ import com.android.systemui.util.animation.TransitionLayout
class RecommendationViewHolder private constructor(itemView: View) {
val recommendations = itemView as TransitionLayout
// Recommendation screen
val mediaCoverItems = listOf<ImageView>(
itemView.requireViewById(R.id.media_cover1),
itemView.requireViewById(R.id.media_cover2),
@@ -49,16 +52,27 @@ class RecommendationViewHolder private constructor(itemView: View) {
R.id.media_logo3,
R.id.media_logo4)
// Settings/Guts screen
val cancel = itemView.requireViewById<View>(R.id.cancel)
val dismiss = itemView.requireViewById<ViewGroup>(R.id.dismiss)
val dismissLabel = dismiss.getChildAt(0)
val recommendationText = itemView.requireViewById<TextView>(R.id.recommendation_text)
val settings = itemView.requireViewById<View>(R.id.settings)
init {
(recommendations.background as IlluminationDrawable).let { background ->
mediaCoverItems.forEach { background.registerLightSource(it) }
mediaLogoItems.forEach { background.registerLightSource(it) }
background.registerLightSource(cancel)
background.registerLightSource(dismiss)
background.registerLightSource(dismissLabel)
background.registerLightSource(settings)
}
}
companion object {
/**
* Creates a PlayerViewHolder.
* Creates a RecommendationViewHolder.
*
* @param inflater LayoutInflater to use to inflate the layout.
* @param parent Parent of inflated view.
@@ -76,5 +90,26 @@ class RecommendationViewHolder private constructor(itemView: View) {
itemView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
return RecommendationViewHolder(itemView)
}
// Res Ids for the control components on the recommendation view.
val controlsIds = setOf(
R.id.media_cover1,
R.id.media_cover2,
R.id.media_cover3,
R.id.media_cover4,
R.id.media_logo1,
R.id.media_logo2,
R.id.media_logo3,
R.id.media_logo4
)
// Res Ids for the components on the guts panel.
val gutsIds = setOf(
R.id.recommendation_text,
R.id.remove_text,
R.id.cancel,
R.id.dismiss,
R.id.settings
)
}
}