Merge changes I3c1f0ebb,I6bf500c5,I86ff54f9,Ia2441b38 into tm-qpr-dev am: 4626fa48c4
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21915059 Change-Id: I0f8a459bf953bbcd9d19402d372c0659a3e463b3 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -17,6 +17,6 @@
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
|
||||
<solid android:color="?androidprv:attr/colorSurface" />
|
||||
<solid android:color="?androidprv:attr/colorAccentSecondary" />
|
||||
<corners android:radius="32dp" />
|
||||
</shape>
|
||||
@@ -20,7 +20,7 @@
|
||||
android:color="?android:textColorPrimary">
|
||||
<item android:id="@android:id/background">
|
||||
<shape>
|
||||
<solid android:color="?androidprv:attr/colorAccentPrimary"/>
|
||||
<solid android:color="@android:color/system_accent1_200"/>
|
||||
<corners android:radius="24dp" />
|
||||
</shape>
|
||||
</item>
|
||||
@@ -29,8 +29,8 @@
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/media_ttt_chip_outer_padding"
|
||||
android:background="@drawable/media_ttt_chip_background"
|
||||
android:padding="@dimen/chipbar_outer_padding"
|
||||
android:background="@drawable/chipbar_background"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginStart="@dimen/notification_side_paddings"
|
||||
android:layout_marginEnd="@dimen/notification_side_paddings"
|
||||
@@ -43,8 +43,8 @@
|
||||
|
||||
<com.android.internal.widget.CachingIconView
|
||||
android:id="@+id/start_icon"
|
||||
android:layout_width="@dimen/media_ttt_app_icon_size"
|
||||
android:layout_height="@dimen/media_ttt_app_icon_size"
|
||||
android:layout_width="@dimen/chipbar_start_icon_size"
|
||||
android:layout_height="@dimen/chipbar_start_icon_size"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:alpha="0.0"
|
||||
/>
|
||||
@@ -54,47 +54,46 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textSize="@dimen/media_ttt_text_size"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="@dimen/chipbar_text_size"
|
||||
android:textColor="@android:color/system_accent2_900"
|
||||
android:alpha="0.0"
|
||||
/>
|
||||
|
||||
<!-- At most one of [loading, failure_icon, undo] will be visible at a time. -->
|
||||
<ImageView
|
||||
android:id="@+id/loading"
|
||||
android:layout_width="@dimen/media_ttt_status_icon_size"
|
||||
android:layout_height="@dimen/media_ttt_status_icon_size"
|
||||
android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
|
||||
android:layout_width="@dimen/chipbar_end_icon_size"
|
||||
android:layout_height="@dimen/chipbar_end_icon_size"
|
||||
android:layout_marginStart="@dimen/chipbar_end_item_start_margin"
|
||||
android:src="@drawable/ic_progress_activity"
|
||||
android:tint="?androidprv:attr/colorAccentPrimaryVariant"
|
||||
android:tint="@android:color/system_accent2_700"
|
||||
android:alpha="0.0"
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/error"
|
||||
android:layout_width="@dimen/media_ttt_status_icon_size"
|
||||
android:layout_height="@dimen/media_ttt_status_icon_size"
|
||||
android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
|
||||
android:layout_width="@dimen/chipbar_end_icon_size"
|
||||
android:layout_height="@dimen/chipbar_end_icon_size"
|
||||
android:layout_marginStart="@dimen/chipbar_end_item_start_margin"
|
||||
android:src="@drawable/ic_warning"
|
||||
android:tint="@color/GM2_red_500"
|
||||
android:tint="@color/GM2_red_600"
|
||||
android:alpha="0.0"
|
||||
/>
|
||||
|
||||
<!-- TODO(b/245610654): Re-name all the media-specific dimens to chipbar dimens instead. -->
|
||||
<TextView
|
||||
android:id="@+id/end_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?androidprv:attr/textColorOnAccent"
|
||||
android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
|
||||
android:textSize="@dimen/media_ttt_text_size"
|
||||
android:paddingStart="@dimen/media_ttt_chip_outer_padding"
|
||||
android:paddingEnd="@dimen/media_ttt_chip_outer_padding"
|
||||
android:paddingTop="@dimen/media_ttt_undo_button_vertical_padding"
|
||||
android:paddingBottom="@dimen/media_ttt_undo_button_vertical_padding"
|
||||
android:layout_marginTop="@dimen/media_ttt_undo_button_vertical_negative_margin"
|
||||
android:layout_marginBottom="@dimen/media_ttt_undo_button_vertical_negative_margin"
|
||||
android:background="@drawable/media_ttt_undo_background"
|
||||
android:layout_marginStart="@dimen/chipbar_end_item_start_margin"
|
||||
android:textSize="@dimen/chipbar_text_size"
|
||||
android:paddingStart="@dimen/chipbar_outer_padding"
|
||||
android:paddingEnd="@dimen/chipbar_outer_padding"
|
||||
android:paddingTop="@dimen/chipbar_end_button_vertical_padding"
|
||||
android:paddingBottom="@dimen/chipbar_end_button_vertical_padding"
|
||||
android:layout_marginTop="@dimen/chipbar_end_button_vertical_negative_margin"
|
||||
android:layout_marginBottom="@dimen/chipbar_end_button_vertical_negative_margin"
|
||||
android:background="@drawable/chipbar_end_button_background"
|
||||
android:alpha="0.0"
|
||||
/>
|
||||
|
||||
|
||||
@@ -161,6 +161,7 @@
|
||||
|
||||
<color name="GM2_red_300">#F28B82</color>
|
||||
<color name="GM2_red_500">#EA4335</color>
|
||||
<color name="GM2_red_600">#B3261E</color>
|
||||
<color name="GM2_red_700">#C5221F</color>
|
||||
|
||||
<color name="GM2_blue_300">#8AB4F8</color>
|
||||
|
||||
@@ -1111,14 +1111,15 @@
|
||||
<dimen name="qs_media_rec_album_title_bottom_margin">22dp</dimen>
|
||||
<dimen name="qs_media_rec_album_subtitle_height">12dp</dimen>
|
||||
|
||||
<!-- Media tap-to-transfer chip for sender device -->
|
||||
<dimen name="media_ttt_chip_outer_padding">16dp</dimen>
|
||||
<dimen name="media_ttt_text_size">16sp</dimen>
|
||||
<dimen name="media_ttt_app_icon_size">24dp</dimen>
|
||||
<dimen name="media_ttt_status_icon_size">20dp</dimen>
|
||||
<dimen name="media_ttt_undo_button_vertical_padding">8dp</dimen>
|
||||
<dimen name="media_ttt_undo_button_vertical_negative_margin">-8dp</dimen>
|
||||
<dimen name="media_ttt_last_item_start_margin">12dp</dimen>
|
||||
<!-- Chipbar -->
|
||||
<!-- (Used for media tap-to-transfer chip for sender device and active unlock) -->
|
||||
<dimen name="chipbar_outer_padding">16dp</dimen>
|
||||
<dimen name="chipbar_text_size">16sp</dimen>
|
||||
<dimen name="chipbar_start_icon_size">24dp</dimen>
|
||||
<dimen name="chipbar_end_icon_size">20dp</dimen>
|
||||
<dimen name="chipbar_end_button_vertical_padding">8dp</dimen>
|
||||
<dimen name="chipbar_end_button_vertical_negative_margin">-8dp</dimen>
|
||||
<dimen name="chipbar_end_item_start_margin">12dp</dimen>
|
||||
|
||||
<!-- Media tap-to-transfer chip for receiver device -->
|
||||
<dimen name="media_ttt_icon_size_receiver">112dp</dimen>
|
||||
|
||||
@@ -23,7 +23,6 @@ import androidx.annotation.StringRes
|
||||
import com.android.internal.logging.UiEventLogger
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.common.shared.model.Text
|
||||
import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
|
||||
|
||||
/**
|
||||
* A class enumerating all the possible states of the media tap-to-transfer chip on the sender
|
||||
@@ -34,8 +33,8 @@ import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
|
||||
* state should not have the chip be displayed.
|
||||
* @property transferStatus the transfer status that the chip state represents.
|
||||
* @property endItem the item that should be displayed in the end section of the chip.
|
||||
* @property timeout the amount of time this chip should display on the screen before it times out
|
||||
* and disappears.
|
||||
* @property timeoutLength how long the chip should display on the screen before it times out and
|
||||
* disappears.
|
||||
*/
|
||||
enum class ChipStateSender(
|
||||
@StatusBarManager.MediaTransferSenderState val stateInt: Int,
|
||||
@@ -43,7 +42,7 @@ enum class ChipStateSender(
|
||||
@StringRes val stringResId: Int?,
|
||||
val transferStatus: TransferStatus,
|
||||
val endItem: SenderEndItem?,
|
||||
val timeout: Int = DEFAULT_TIMEOUT_MILLIS,
|
||||
val timeoutLength: TimeoutLength = TimeoutLength.DEFAULT,
|
||||
) {
|
||||
/**
|
||||
* A state representing that the two devices are close but not close enough to *start* a cast to
|
||||
@@ -56,6 +55,9 @@ enum class ChipStateSender(
|
||||
R.string.media_move_closer_to_start_cast,
|
||||
transferStatus = TransferStatus.NOT_STARTED,
|
||||
endItem = null,
|
||||
// Give this view more time in case the loading view takes a bit to come in. (We don't want
|
||||
// this view to disappear and then the loading view to appear quickly afterwards.)
|
||||
timeoutLength = TimeoutLength.LONG,
|
||||
) {
|
||||
override fun isValidNextState(nextState: ChipStateSender): Boolean {
|
||||
return nextState == FAR_FROM_RECEIVER ||
|
||||
@@ -75,6 +77,7 @@ enum class ChipStateSender(
|
||||
R.string.media_move_closer_to_end_cast,
|
||||
transferStatus = TransferStatus.NOT_STARTED,
|
||||
endItem = null,
|
||||
timeoutLength = TimeoutLength.LONG,
|
||||
) {
|
||||
override fun isValidNextState(nextState: ChipStateSender): Boolean {
|
||||
return nextState == FAR_FROM_RECEIVER ||
|
||||
@@ -92,7 +95,9 @@ enum class ChipStateSender(
|
||||
R.string.media_transfer_playing_different_device,
|
||||
transferStatus = TransferStatus.IN_PROGRESS,
|
||||
endItem = SenderEndItem.Loading,
|
||||
timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
|
||||
// Give this view more time in case the succeeded/failed view takes a bit to come in. (We
|
||||
// don't want this view to disappear and then the next view to appear quickly afterwards.)
|
||||
timeoutLength = TimeoutLength.LONG,
|
||||
) {
|
||||
override fun isValidNextState(nextState: ChipStateSender): Boolean {
|
||||
return nextState == FAR_FROM_RECEIVER ||
|
||||
@@ -111,7 +116,7 @@ enum class ChipStateSender(
|
||||
R.string.media_transfer_playing_this_device,
|
||||
transferStatus = TransferStatus.IN_PROGRESS,
|
||||
endItem = SenderEndItem.Loading,
|
||||
timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
|
||||
timeoutLength = TimeoutLength.LONG,
|
||||
) {
|
||||
override fun isValidNextState(nextState: ChipStateSender): Boolean {
|
||||
return nextState == FAR_FROM_RECEIVER ||
|
||||
@@ -325,9 +330,16 @@ sealed class SenderEndItem {
|
||||
) : SenderEndItem()
|
||||
}
|
||||
|
||||
// Give the Transfer*Triggered states a longer timeout since those states represent an active
|
||||
// process and we should keep the user informed about it as long as possible (but don't allow it to
|
||||
// continue indefinitely).
|
||||
private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 30000
|
||||
/** Represents how long the chip should be visible before it times out. */
|
||||
enum class TimeoutLength {
|
||||
/** A default timeout used for temporary displays at the top of the screen. */
|
||||
DEFAULT,
|
||||
/**
|
||||
* A longer timeout. Should be used when the status is pending (e.g. loading), so that the user
|
||||
* remains informed about the process for longer and so that the UI has more time to resolve the
|
||||
* pending state before disappearing.
|
||||
*/
|
||||
LONG,
|
||||
}
|
||||
|
||||
private const val TAG = "ChipStateSender"
|
||||
|
||||
@@ -56,6 +56,9 @@ constructor(
|
||||
private val uiEventLogger: MediaTttSenderUiEventLogger,
|
||||
) : CoreStartable, Dumpable {
|
||||
|
||||
// Since the media transfer display is similar to a heads-up notification, use the same timeout.
|
||||
private val defaultTimeout = context.resources.getInteger(R.integer.heads_up_notification_decay)
|
||||
|
||||
// A map to store current chip state per id.
|
||||
private var stateMap: MutableMap<String, ChipStateSender> = mutableMapOf()
|
||||
|
||||
@@ -165,6 +168,12 @@ constructor(
|
||||
logger.logPackageNotFound(packageName)
|
||||
}
|
||||
|
||||
val timeout =
|
||||
when (chipStateSender.timeoutLength) {
|
||||
TimeoutLength.DEFAULT -> defaultTimeout
|
||||
TimeoutLength.LONG -> 2 * defaultTimeout
|
||||
}
|
||||
|
||||
return ChipbarInfo(
|
||||
// Display the app's icon as the start icon
|
||||
startIcon = icon.toTintedIcon(),
|
||||
@@ -191,7 +200,7 @@ constructor(
|
||||
allowSwipeToDismiss = true,
|
||||
windowTitle = MediaTttUtils.WINDOW_TITLE_SENDER,
|
||||
wakeReason = MediaTttUtils.WAKE_REASON_SENDER,
|
||||
timeoutMs = chipStateSender.timeout,
|
||||
timeoutMs = timeout,
|
||||
id = routeInfo.id,
|
||||
priority = ViewPriority.NORMAL,
|
||||
)
|
||||
|
||||
@@ -274,6 +274,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
||||
it.title = newInfo.windowTitle
|
||||
}
|
||||
newView.keepScreenOn = true
|
||||
logger.logViewAddedToWindowManager(displayInfo.info, newView)
|
||||
windowManager.addView(newView, paramsWithTitle)
|
||||
animateViewIn(newView)
|
||||
}
|
||||
@@ -286,6 +287,11 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
||||
val view = checkNotNull(currentDisplayInfo.view) {
|
||||
"First item in activeViews list must have a valid view"
|
||||
}
|
||||
logger.logViewRemovedFromWindowManager(
|
||||
currentDisplayInfo.info,
|
||||
view,
|
||||
isReinflation = true,
|
||||
)
|
||||
windowManager.removeView(view)
|
||||
inflateAndUpdateView(currentDisplayInfo)
|
||||
}
|
||||
@@ -294,6 +300,10 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
||||
override fun onDensityOrFontScaleChanged() {
|
||||
reinflateView()
|
||||
}
|
||||
|
||||
override fun onThemeChanged() {
|
||||
reinflateView()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addCallbacks() {
|
||||
@@ -378,6 +388,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
||||
}
|
||||
displayInfo.view = null // Need other places??
|
||||
animateViewOut(view, removalReason) {
|
||||
logger.logViewRemovedFromWindowManager(displayInfo.info, view)
|
||||
windowManager.removeView(view)
|
||||
displayInfo.wakeLock?.release(displayInfo.info.wakeReason)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.temporarydisplay
|
||||
|
||||
import android.view.View
|
||||
import com.android.systemui.plugins.log.LogBuffer
|
||||
import com.android.systemui.plugins.log.LogLevel
|
||||
|
||||
@@ -141,4 +142,46 @@ open class TemporaryViewLogger<T : TemporaryViewInfo>(
|
||||
{ "Removal of view with id=$str2 is ignored because $str1" }
|
||||
)
|
||||
}
|
||||
|
||||
fun logViewAddedToWindowManager(info: T, view: View) {
|
||||
buffer.log(
|
||||
tag,
|
||||
LogLevel.DEBUG,
|
||||
{
|
||||
str1 = info.id
|
||||
str2 = info.windowTitle
|
||||
str3 = view.javaClass.name
|
||||
int1 = view.getIdForLogging()
|
||||
},
|
||||
{
|
||||
"Adding view to window manager. " +
|
||||
"id=$str1 window=$str2 view=$str3(id=${Integer.toHexString(int1)})"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun logViewRemovedFromWindowManager(info: T, view: View, isReinflation: Boolean = false) {
|
||||
buffer.log(
|
||||
tag,
|
||||
LogLevel.DEBUG,
|
||||
{
|
||||
str1 = info.id
|
||||
str2 = info.windowTitle
|
||||
str3 = view.javaClass.name
|
||||
int1 = view.getIdForLogging()
|
||||
bool1 = isReinflation
|
||||
},
|
||||
{
|
||||
"Removing view from window manager${if (bool1) " due to reinflation" else ""}. " +
|
||||
"id=$str1 window=$str2 view=$str3(id=${Integer.toHexString(int1)})"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun View.getIdForLogging(): Int {
|
||||
// The identityHashCode is guaranteed to be constant for the lifetime of the object.
|
||||
return System.identityHashCode(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
||||
private lateinit var fakeExecutor: FakeExecutor
|
||||
private lateinit var uiEventLoggerFake: UiEventLoggerFake
|
||||
private lateinit var uiEventLogger: MediaTttSenderUiEventLogger
|
||||
private val defaultTimeout = context.resources.getInteger(R.integer.heads_up_notification_decay)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
@@ -1356,6 +1357,92 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
||||
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun almostClose_hasLongTimeout_eventuallyTimesOut() {
|
||||
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenAnswer {
|
||||
it.arguments[0]
|
||||
}
|
||||
|
||||
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
|
||||
routeInfo,
|
||||
null,
|
||||
)
|
||||
|
||||
// WHEN the default timeout has passed
|
||||
fakeClock.advanceTime(defaultTimeout + 1L)
|
||||
|
||||
// THEN the view is still on-screen because it has a long timeout
|
||||
verify(windowManager, never()).removeView(any())
|
||||
|
||||
// WHEN a very long amount of time has passed
|
||||
fakeClock.advanceTime(5L * defaultTimeout)
|
||||
|
||||
// THEN the view does time out
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loading_hasLongTimeout_eventuallyTimesOut() {
|
||||
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenAnswer {
|
||||
it.arguments[0]
|
||||
}
|
||||
|
||||
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
|
||||
routeInfo,
|
||||
null,
|
||||
)
|
||||
|
||||
// WHEN the default timeout has passed
|
||||
fakeClock.advanceTime(defaultTimeout + 1L)
|
||||
|
||||
// THEN the view is still on-screen because it has a long timeout
|
||||
verify(windowManager, never()).removeView(any())
|
||||
|
||||
// WHEN a very long amount of time has passed
|
||||
fakeClock.advanceTime(5L * defaultTimeout)
|
||||
|
||||
// THEN the view does time out
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun succeeded_hasDefaultTimeout() {
|
||||
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenAnswer {
|
||||
it.arguments[0]
|
||||
}
|
||||
|
||||
displayReceiverTriggered()
|
||||
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
|
||||
routeInfo,
|
||||
null,
|
||||
)
|
||||
|
||||
fakeClock.advanceTime(defaultTimeout + 1L)
|
||||
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun failed_hasDefaultTimeout() {
|
||||
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenAnswer {
|
||||
it.arguments[0]
|
||||
}
|
||||
|
||||
displayThisDeviceTriggered()
|
||||
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED,
|
||||
routeInfo,
|
||||
null,
|
||||
)
|
||||
|
||||
fakeClock.advanceTime(defaultTimeout + 1L)
|
||||
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
|
||||
private fun getChipbarView(): ViewGroup {
|
||||
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
|
||||
verify(windowManager).addView(viewCaptor.capture(), any())
|
||||
|
||||
Reference in New Issue
Block a user