Merge changes from topic "b224749799" into tm-dev
* changes: Allow null prev/next buttons to be INVISIBLE instead of GONE Remove logging and nullable return type from getCustomAction
This commit is contained in:
@@ -837,7 +837,14 @@ public class MediaControlPanel {
|
||||
scrubbingTimeViewsEnabled(semanticActions) && hideWhenScrubbing && mIsScrubbing;
|
||||
boolean visible = mediaAction != null && !shouldBeHiddenDueToScrubbing;
|
||||
|
||||
setVisibleAndAlpha(expandedSet, buttonId, visible);
|
||||
int notVisibleValue;
|
||||
if ((buttonId == R.id.actionPrev && semanticActions.getReservePrev())
|
||||
|| (buttonId == R.id.actionNext && semanticActions.getReserveNext())) {
|
||||
notVisibleValue = ConstraintSet.INVISIBLE;
|
||||
} else {
|
||||
notVisibleValue = ConstraintSet.GONE;
|
||||
}
|
||||
setVisibleAndAlpha(expandedSet, buttonId, visible, notVisibleValue);
|
||||
setVisibleAndAlpha(collapsedSet, buttonId, visible && showInCompact);
|
||||
}
|
||||
|
||||
@@ -1177,7 +1184,12 @@ public class MediaControlPanel {
|
||||
}
|
||||
|
||||
private void setVisibleAndAlpha(ConstraintSet set, int actionId, boolean visible) {
|
||||
set.setVisibility(actionId, visible ? ConstraintSet.VISIBLE : ConstraintSet.GONE);
|
||||
setVisibleAndAlpha(set, actionId, visible, ConstraintSet.GONE);
|
||||
}
|
||||
|
||||
private void setVisibleAndAlpha(ConstraintSet set, int actionId, boolean visible,
|
||||
int notVisibleValue) {
|
||||
set.setVisibility(actionId, visible ? ConstraintSet.VISIBLE : notVisibleValue);
|
||||
set.setAlpha(actionId, visible ? 1.0f : 0.0f);
|
||||
}
|
||||
|
||||
|
||||
@@ -149,23 +149,31 @@ data class MediaButton(
|
||||
/**
|
||||
* Play/pause button
|
||||
*/
|
||||
var playOrPause: MediaAction? = null,
|
||||
val playOrPause: MediaAction? = null,
|
||||
/**
|
||||
* Next button, or custom action
|
||||
*/
|
||||
var nextOrCustom: MediaAction? = null,
|
||||
val nextOrCustom: MediaAction? = null,
|
||||
/**
|
||||
* Previous button, or custom action
|
||||
*/
|
||||
var prevOrCustom: MediaAction? = null,
|
||||
val prevOrCustom: MediaAction? = null,
|
||||
/**
|
||||
* First custom action space
|
||||
*/
|
||||
var custom0: MediaAction? = null,
|
||||
val custom0: MediaAction? = null,
|
||||
/**
|
||||
* Second custom action space
|
||||
*/
|
||||
var custom1: MediaAction? = null
|
||||
val custom1: MediaAction? = null,
|
||||
/**
|
||||
* Whether to reserve the empty space when the nextOrCustom is null
|
||||
*/
|
||||
val reserveNext: Boolean = false,
|
||||
/**
|
||||
* Whether to reserve the empty space when the prevOrCustom is null
|
||||
*/
|
||||
val reservePrev: Boolean = false
|
||||
) {
|
||||
fun getActionById(id: Int): MediaAction? {
|
||||
return when (id) {
|
||||
|
||||
@@ -173,10 +173,6 @@ class MediaDataManager(
|
||||
// Maximum number of actions allowed in expanded view
|
||||
@JvmField
|
||||
val MAX_NOTIFICATION_ACTIONS = MediaViewHolder.genericButtonIds.size
|
||||
|
||||
/** Maximum number of [PlaybackState.CustomAction] buttons supported */
|
||||
@JvmField
|
||||
val MAX_CUSTOM_ACTIONS = 4
|
||||
}
|
||||
|
||||
private val themeText = com.android.settingslib.Utils.getColorAttr(context,
|
||||
@@ -795,71 +791,74 @@ class MediaDataManager(
|
||||
*/
|
||||
private fun createActionsFromState(packageName: String, controller: MediaController):
|
||||
MediaButton? {
|
||||
val actions = MediaButton()
|
||||
controller.playbackState?.let { state ->
|
||||
// First, check for standard actions
|
||||
actions.playOrPause = if (isConnectingState(state.state)) {
|
||||
// Spinner needs to be animating to render anything. Start it here.
|
||||
val drawable = context.getDrawable(
|
||||
com.android.internal.R.drawable.progress_small_material)
|
||||
(drawable as Animatable).start()
|
||||
MediaAction(
|
||||
drawable,
|
||||
null, // no action to perform when clicked
|
||||
context.getString(R.string.controls_media_button_connecting),
|
||||
context.getDrawable(R.drawable.ic_media_connecting_container),
|
||||
// Specify a rebind id to prevent the spinner from restarting on later binds.
|
||||
com.android.internal.R.drawable.progress_small_material
|
||||
)
|
||||
} else if (isPlayingState(state.state)) {
|
||||
getStandardAction(controller, state.actions, PlaybackState.ACTION_PAUSE)
|
||||
} else {
|
||||
getStandardAction(controller, state.actions, PlaybackState.ACTION_PLAY)
|
||||
}
|
||||
val prevButton = getStandardAction(controller, state.actions,
|
||||
PlaybackState.ACTION_SKIP_TO_PREVIOUS)
|
||||
val nextButton = getStandardAction(controller, state.actions,
|
||||
PlaybackState.ACTION_SKIP_TO_NEXT)
|
||||
|
||||
// Then, check for custom actions
|
||||
val customActions = MutableList<MediaAction?>(MAX_CUSTOM_ACTIONS) { null }
|
||||
var customCount = 0
|
||||
for (i in 0..(MAX_CUSTOM_ACTIONS - 1)) {
|
||||
getCustomAction(state, packageName, controller, customCount)?.let {
|
||||
customActions[customCount++] = it
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, assign the remaining button slots: play/pause A B C D
|
||||
// A = previous, else custom action (if not reserved)
|
||||
// B = next, else custom action (if not reserved)
|
||||
// C and D are always custom actions
|
||||
val reservePrev = controller.extras?.getBoolean(
|
||||
MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV) == true
|
||||
val reserveNext = controller.extras?.getBoolean(
|
||||
MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT) == true
|
||||
var customIdx = 0
|
||||
|
||||
actions.prevOrCustom = if (prevButton != null) {
|
||||
prevButton
|
||||
} else if (!reservePrev) {
|
||||
customActions[customIdx++]
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
actions.nextOrCustom = if (nextButton != null) {
|
||||
nextButton
|
||||
} else if (!reserveNext) {
|
||||
customActions[customIdx++]
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
actions.custom0 = customActions[customIdx++]
|
||||
actions.custom1 = customActions[customIdx++]
|
||||
val state = controller.playbackState
|
||||
if (state == null) {
|
||||
return MediaButton()
|
||||
}
|
||||
return actions
|
||||
// First, check for} standard actions
|
||||
val playOrPause = if (isConnectingState(state.state)) {
|
||||
// Spinner needs to be animating to render anything. Start it here.
|
||||
val drawable = context.getDrawable(
|
||||
com.android.internal.R.drawable.progress_small_material)
|
||||
(drawable as Animatable).start()
|
||||
MediaAction(
|
||||
drawable,
|
||||
null, // no action to perform when clicked
|
||||
context.getString(R.string.controls_media_button_connecting),
|
||||
context.getDrawable(R.drawable.ic_media_connecting_container),
|
||||
// Specify a rebind id to prevent the spinner from restarting on later binds.
|
||||
com.android.internal.R.drawable.progress_small_material
|
||||
)
|
||||
} else if (isPlayingState(state.state)) {
|
||||
getStandardAction(controller, state.actions, PlaybackState.ACTION_PAUSE)
|
||||
} else {
|
||||
getStandardAction(controller, state.actions, PlaybackState.ACTION_PLAY)
|
||||
}
|
||||
val prevButton = getStandardAction(controller, state.actions,
|
||||
PlaybackState.ACTION_SKIP_TO_PREVIOUS)
|
||||
val nextButton = getStandardAction(controller, state.actions,
|
||||
PlaybackState.ACTION_SKIP_TO_NEXT)
|
||||
|
||||
// Then, create a way to build any custom actions that will be needed
|
||||
val customActions = state.customActions.asSequence().filterNotNull().map {
|
||||
getCustomAction(state, packageName, controller, it)
|
||||
}.iterator()
|
||||
fun nextCustomAction() = if (customActions.hasNext()) customActions.next() else null
|
||||
|
||||
// Finally, assign the remaining button slots: play/pause A B C D
|
||||
// A = previous, else custom action (if not reserved)
|
||||
// B = next, else custom action (if not reserved)
|
||||
// C and D are always custom actions
|
||||
val reservePrev = controller.extras?.getBoolean(
|
||||
MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV) == true
|
||||
val reserveNext = controller.extras?.getBoolean(
|
||||
MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT) == true
|
||||
|
||||
val prevOrCustom = if (prevButton != null) {
|
||||
prevButton
|
||||
} else if (!reservePrev) {
|
||||
nextCustomAction()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val nextOrCustom = if (nextButton != null) {
|
||||
nextButton
|
||||
} else if (!reserveNext) {
|
||||
nextCustomAction()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
return MediaButton(
|
||||
playOrPause,
|
||||
nextOrCustom,
|
||||
prevOrCustom,
|
||||
nextCustomAction(),
|
||||
nextCustomAction(),
|
||||
reserveNext,
|
||||
reservePrev
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -938,18 +937,12 @@ class MediaDataManager(
|
||||
state: PlaybackState,
|
||||
packageName: String,
|
||||
controller: MediaController,
|
||||
index: Int
|
||||
): MediaAction? {
|
||||
if (state.customActions.size <= index || state.customActions[index] == null) {
|
||||
if (DEBUG) { Log.d(TAG, "not enough actions or action was null at $index") }
|
||||
return null
|
||||
}
|
||||
|
||||
val it = state.customActions[index]
|
||||
customAction: PlaybackState.CustomAction
|
||||
): MediaAction {
|
||||
return MediaAction(
|
||||
Icon.createWithResource(packageName, it.icon).loadDrawable(context),
|
||||
{ controller.transportControls.sendCustomAction(it, it.extras) },
|
||||
it.name,
|
||||
Icon.createWithResource(packageName, customAction.icon).loadDrawable(context),
|
||||
{ controller.transportControls.sendCustomAction(customAction, customAction.extras) },
|
||||
customAction.name,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
@@ -448,6 +448,64 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
verify(expandedSet).setVisibility(R.id.action4, ConstraintSet.GONE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bindSemanticActions_reservedPrev() {
|
||||
val icon = context.getDrawable(android.R.drawable.ic_media_play)
|
||||
val bg = context.getDrawable(R.drawable.qs_media_round_button_background)
|
||||
|
||||
// Setup button state: no prev or next button and their slots reserved
|
||||
val semanticActions = MediaButton(
|
||||
playOrPause = MediaAction(icon, Runnable {}, "play", bg),
|
||||
nextOrCustom = null,
|
||||
prevOrCustom = null,
|
||||
custom0 = MediaAction(icon, null, "custom 0", bg),
|
||||
custom1 = MediaAction(icon, null, "custom 1", bg),
|
||||
false,
|
||||
true
|
||||
)
|
||||
val state = mediaData.copy(semanticActions = semanticActions)
|
||||
|
||||
player.attachPlayer(viewHolder)
|
||||
player.bindPlayer(state, PACKAGE)
|
||||
|
||||
assertThat(actionPrev.isEnabled()).isFalse()
|
||||
assertThat(actionPrev.drawable).isNull()
|
||||
verify(expandedSet).setVisibility(R.id.actionPrev, ConstraintSet.INVISIBLE)
|
||||
|
||||
assertThat(actionNext.isEnabled()).isFalse()
|
||||
assertThat(actionNext.drawable).isNull()
|
||||
verify(expandedSet).setVisibility(R.id.actionNext, ConstraintSet.GONE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bindSemanticActions_reservedNext() {
|
||||
val icon = context.getDrawable(android.R.drawable.ic_media_play)
|
||||
val bg = context.getDrawable(R.drawable.qs_media_round_button_background)
|
||||
|
||||
// Setup button state: no prev or next button and their slots reserved
|
||||
val semanticActions = MediaButton(
|
||||
playOrPause = MediaAction(icon, Runnable {}, "play", bg),
|
||||
nextOrCustom = null,
|
||||
prevOrCustom = null,
|
||||
custom0 = MediaAction(icon, null, "custom 0", bg),
|
||||
custom1 = MediaAction(icon, null, "custom 1", bg),
|
||||
true,
|
||||
false
|
||||
)
|
||||
val state = mediaData.copy(semanticActions = semanticActions)
|
||||
|
||||
player.attachPlayer(viewHolder)
|
||||
player.bindPlayer(state, PACKAGE)
|
||||
|
||||
assertThat(actionPrev.isEnabled()).isFalse()
|
||||
assertThat(actionPrev.drawable).isNull()
|
||||
verify(expandedSet).setVisibility(R.id.actionPrev, ConstraintSet.GONE)
|
||||
|
||||
assertThat(actionNext.isEnabled()).isFalse()
|
||||
assertThat(actionNext.drawable).isNull()
|
||||
verify(expandedSet).setVisibility(R.id.actionNext, ConstraintSet.INVISIBLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bind_seekBarDisabled_seekBarVisibilityIsSetToInvisible() {
|
||||
whenever(seekBarViewModel.getEnabled()).thenReturn(false)
|
||||
|
||||
@@ -838,6 +838,9 @@ class MediaDataManagerTest : SysuiTestCase() {
|
||||
|
||||
assertThat(actions.custom1).isNotNull()
|
||||
assertThat(actions.custom1!!.contentDescription).isEqualTo(customDesc[1])
|
||||
|
||||
assertThat(actions.reserveNext).isTrue()
|
||||
assertThat(actions.reservePrev).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user