Merge "Add the guts control panel to Smartspace media recommendation card." into sc-dev
This commit is contained in:
@@ -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>
|
||||
@@ -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 */)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */,
|
||||
|
||||
@@ -191,7 +191,7 @@ class MediaDataFilter @Inject constructor(
|
||||
mediaDataManager.setTimedOut(it, timedOut = true, forceUpdate = true)
|
||||
}
|
||||
if (hasSmartspace) {
|
||||
mediaDataManager.dismissSmartspaceRecommendation()
|
||||
mediaDataManager.dismissSmartspaceRecommendation(0L /* delay */)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user