Merge "[Chipbar] Update ChipbarCoordinator to have a generic API to display chipbars." into tm-qpr-dev am: ac09efc0b4

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20177091

Change-Id: I287ca2dfc35387f42549c1d0ac8922f835a0e1c0
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Caitlin Shkuratov
2022-10-20 16:36:45 +00:00
committed by Automerger Merge Worker
14 changed files with 852 additions and 619 deletions

View File

@@ -832,7 +832,6 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
-packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
-packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt

View File

@@ -19,12 +19,12 @@
<com.android.systemui.temporarydisplay.chipbar.ChipbarRootView <com.android.systemui.temporarydisplay.chipbar.ChipbarRootView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/media_ttt_sender_chip" android:id="@+id/chipbar_root_view"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<LinearLayout <LinearLayout
android:id="@+id/media_ttt_sender_chip_inner" android:id="@+id/chipbar_inner"
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -39,7 +39,7 @@
> >
<com.android.internal.widget.CachingIconView <com.android.internal.widget.CachingIconView
android:id="@+id/app_icon" android:id="@+id/start_icon"
android:layout_width="@dimen/media_ttt_app_icon_size" android:layout_width="@dimen/media_ttt_app_icon_size"
android:layout_height="@dimen/media_ttt_app_icon_size" android:layout_height="@dimen/media_ttt_app_icon_size"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
@@ -69,7 +69,7 @@
/> />
<ImageView <ImageView
android:id="@+id/failure_icon" android:id="@+id/error"
android:layout_width="@dimen/media_ttt_status_icon_size" android:layout_width="@dimen/media_ttt_status_icon_size"
android:layout_height="@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_marginStart="@dimen/media_ttt_last_item_start_margin"
@@ -78,11 +78,11 @@
android:alpha="0.0" android:alpha="0.0"
/> />
<!-- TODO(b/245610654): Re-name all the media-specific dimens to chipbar dimens instead. -->
<TextView <TextView
android:id="@+id/undo" android:id="@+id/end_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/media_transfer_undo"
android:textColor="?androidprv:attr/textColorOnAccent" android:textColor="?androidprv:attr/textColorOnAccent"
android:layout_marginStart="@dimen/media_ttt_last_item_start_margin" android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
android:textSize="@dimen/media_ttt_text_size" android:textSize="@dimen/media_ttt_text_size"

View File

@@ -17,6 +17,7 @@
package com.android.systemui.common.shared.model package com.android.systemui.common.shared.model
import android.annotation.StringRes import android.annotation.StringRes
import android.content.Context
/** /**
* Models a content description, that can either be already [loaded][ContentDescription.Loaded] or * Models a content description, that can either be already [loaded][ContentDescription.Loaded] or
@@ -30,4 +31,20 @@ sealed class ContentDescription {
data class Resource( data class Resource(
@StringRes val res: Int, @StringRes val res: Int,
) : ContentDescription() ) : ContentDescription()
companion object {
/**
* Returns the loaded content description string, or null if we don't have one.
*
* Prefer [com.android.systemui.common.ui.binder.ContentDescriptionViewBinder.bind] over
* this method. This should only be used for testing or concatenation purposes.
*/
fun ContentDescription?.loadContentDescription(context: Context): String? {
return when (this) {
null -> null
is Loaded -> this.description
is Resource -> context.getString(this.res)
}
}
}
} }

View File

@@ -18,6 +18,7 @@
package com.android.systemui.common.shared.model package com.android.systemui.common.shared.model
import android.annotation.StringRes import android.annotation.StringRes
import android.content.Context
/** /**
* Models a text, that can either be already [loaded][Text.Loaded] or be a [reference] * Models a text, that can either be already [loaded][Text.Loaded] or be a [reference]
@@ -31,4 +32,20 @@ sealed class Text {
data class Resource( data class Resource(
@StringRes val res: Int, @StringRes val res: Int,
) : Text() ) : Text()
companion object {
/**
* Returns the loaded test string, or null if we don't have one.
*
* Prefer [com.android.systemui.common.ui.binder.TextViewBinder.bind] over this method. This
* should only be used for testing or concatenation purposes.
*/
fun Text?.loadText(context: Context): String? {
return when (this) {
null -> null
is Loaded -> this.text
is Resource -> context.getString(this.res)
}
}
}
} }

View File

@@ -21,6 +21,8 @@ import android.content.pm.PackageManager
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import com.android.settingslib.Utils import com.android.settingslib.Utils
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
/** Utility methods for media tap-to-transfer. */ /** Utility methods for media tap-to-transfer. */
class MediaTttUtils { class MediaTttUtils {
@@ -30,6 +32,23 @@ class MediaTttUtils {
const val WINDOW_TITLE = "Media Transfer Chip View" const val WINDOW_TITLE = "Media Transfer Chip View"
const val WAKE_REASON = "MEDIA_TRANSFER_ACTIVATED" const val WAKE_REASON = "MEDIA_TRANSFER_ACTIVATED"
/**
* Returns the information needed to display the icon in [Icon] form.
*
* See [getIconInfoFromPackageName].
*/
fun getIconFromPackageName(
context: Context,
appPackageName: String?,
logger: MediaTttLogger,
): Icon {
val iconInfo = getIconInfoFromPackageName(context, appPackageName, logger)
return Icon.Loaded(
iconInfo.drawable,
ContentDescription.Loaded(iconInfo.contentDescription)
)
}
/** /**
* Returns the information needed to display the icon. * Returns the information needed to display the icon.
* *

View File

@@ -18,17 +18,12 @@ package com.android.systemui.media.taptotransfer.sender
import android.app.StatusBarManager import android.app.StatusBarManager
import android.content.Context import android.content.Context
import android.media.MediaRoute2Info
import android.util.Log import android.util.Log
import android.view.View
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.android.internal.logging.UiEventLogger import com.android.internal.logging.UiEventLogger
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.plugins.FalsingManager import com.android.systemui.common.shared.model.Text
import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
import com.android.systemui.temporarydisplay.chipbar.ChipSenderInfo
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
/** /**
* A class enumerating all the possible states of the media tap-to-transfer chip on the sender * A class enumerating all the possible states of the media tap-to-transfer chip on the sender
@@ -38,6 +33,7 @@ import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
* @property stringResId the res ID of the string that should be displayed in the chip. Null if the * @property stringResId the res ID of the string that should be displayed in the chip. Null if the
* state should not have the chip be displayed. * state should not have the chip be displayed.
* @property transferStatus the transfer status that the chip state represents. * @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 * @property timeout the amount of time this chip should display on the screen before it times out
* and disappears. * and disappears.
*/ */
@@ -46,6 +42,7 @@ enum class ChipStateSender(
val uiEvent: UiEventLogger.UiEventEnum, val uiEvent: UiEventLogger.UiEventEnum,
@StringRes val stringResId: Int?, @StringRes val stringResId: Int?,
val transferStatus: TransferStatus, val transferStatus: TransferStatus,
val endItem: SenderEndItem?,
val timeout: Long = DEFAULT_TIMEOUT_MILLIS val timeout: Long = DEFAULT_TIMEOUT_MILLIS
) { ) {
/** /**
@@ -58,6 +55,7 @@ enum class ChipStateSender(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_START_CAST, MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_START_CAST,
R.string.media_move_closer_to_start_cast, R.string.media_move_closer_to_start_cast,
transferStatus = TransferStatus.NOT_STARTED, transferStatus = TransferStatus.NOT_STARTED,
endItem = null,
), ),
/** /**
@@ -71,6 +69,7 @@ enum class ChipStateSender(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_END_CAST, MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_END_CAST,
R.string.media_move_closer_to_end_cast, R.string.media_move_closer_to_end_cast,
transferStatus = TransferStatus.NOT_STARTED, transferStatus = TransferStatus.NOT_STARTED,
endItem = null,
), ),
/** /**
@@ -82,6 +81,7 @@ enum class ChipStateSender(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_TRIGGERED, MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_TRIGGERED,
R.string.media_transfer_playing_different_device, R.string.media_transfer_playing_different_device,
transferStatus = TransferStatus.IN_PROGRESS, transferStatus = TransferStatus.IN_PROGRESS,
endItem = SenderEndItem.Loading,
timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
), ),
@@ -94,6 +94,7 @@ enum class ChipStateSender(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_TRIGGERED, MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
R.string.media_transfer_playing_this_device, R.string.media_transfer_playing_this_device,
transferStatus = TransferStatus.IN_PROGRESS, transferStatus = TransferStatus.IN_PROGRESS,
endItem = SenderEndItem.Loading,
timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
), ),
@@ -105,36 +106,13 @@ enum class ChipStateSender(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_SUCCEEDED, MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_SUCCEEDED,
R.string.media_transfer_playing_different_device, R.string.media_transfer_playing_different_device,
transferStatus = TransferStatus.SUCCEEDED, transferStatus = TransferStatus.SUCCEEDED,
) { endItem = SenderEndItem.UndoButton(
override fun undoClickListener( uiEventOnClick =
chipbarCoordinator: ChipbarCoordinator, MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_RECEIVER_CLICKED,
routeInfo: MediaRoute2Info, newState =
undoCallback: IUndoMediaTransferCallback?, StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED
uiEventLogger: MediaTttSenderUiEventLogger, ),
falsingManager: FalsingManager, ),
): View.OnClickListener? {
if (undoCallback == null) {
return null
}
return View.OnClickListener {
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@OnClickListener
uiEventLogger.logUndoClicked(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_RECEIVER_CLICKED
)
undoCallback.onUndoTriggered()
// The external service should eventually send us a TransferToThisDeviceTriggered
// state, but that may take too long to go through the binder and the user may be
// confused as to why the UI hasn't changed yet. So, we immediately change the UI
// here.
chipbarCoordinator.displayView(
ChipSenderInfo(
TRANSFER_TO_THIS_DEVICE_TRIGGERED, routeInfo, undoCallback
)
)
}
}
},
/** /**
* A state representing that a transfer back to this device has been successfully completed. * A state representing that a transfer back to this device has been successfully completed.
@@ -144,36 +122,13 @@ enum class ChipStateSender(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_SUCCEEDED, MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
R.string.media_transfer_playing_this_device, R.string.media_transfer_playing_this_device,
transferStatus = TransferStatus.SUCCEEDED, transferStatus = TransferStatus.SUCCEEDED,
) { endItem = SenderEndItem.UndoButton(
override fun undoClickListener( uiEventOnClick =
chipbarCoordinator: ChipbarCoordinator, MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_THIS_DEVICE_CLICKED,
routeInfo: MediaRoute2Info, newState =
undoCallback: IUndoMediaTransferCallback?, StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED
uiEventLogger: MediaTttSenderUiEventLogger, ),
falsingManager: FalsingManager, ),
): View.OnClickListener? {
if (undoCallback == null) {
return null
}
return View.OnClickListener {
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@OnClickListener
uiEventLogger.logUndoClicked(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_THIS_DEVICE_CLICKED
)
undoCallback.onUndoTriggered()
// The external service should eventually send us a TransferToReceiverTriggered
// state, but that may take too long to go through the binder and the user may be
// confused as to why the UI hasn't changed yet. So, we immediately change the UI
// here.
chipbarCoordinator.displayView(
ChipSenderInfo(
TRANSFER_TO_RECEIVER_TRIGGERED, routeInfo, undoCallback
)
)
}
}
},
/** A state representing that a transfer to the receiver device has failed. */ /** A state representing that a transfer to the receiver device has failed. */
TRANSFER_TO_RECEIVER_FAILED( TRANSFER_TO_RECEIVER_FAILED(
@@ -181,6 +136,7 @@ enum class ChipStateSender(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_FAILED, MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_FAILED,
R.string.media_transfer_failed, R.string.media_transfer_failed,
transferStatus = TransferStatus.FAILED, transferStatus = TransferStatus.FAILED,
endItem = SenderEndItem.Error,
), ),
/** A state representing that a transfer back to this device has failed. */ /** A state representing that a transfer back to this device has failed. */
@@ -189,6 +145,7 @@ enum class ChipStateSender(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_FAILED, MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_FAILED,
R.string.media_transfer_failed, R.string.media_transfer_failed,
transferStatus = TransferStatus.FAILED, transferStatus = TransferStatus.FAILED,
endItem = SenderEndItem.Error,
), ),
/** A state representing that this device is far away from any receiver device. */ /** A state representing that this device is far away from any receiver device. */
@@ -197,37 +154,27 @@ enum class ChipStateSender(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_FAR_FROM_RECEIVER, MediaTttSenderUiEvents.MEDIA_TTT_SENDER_FAR_FROM_RECEIVER,
stringResId = null, stringResId = null,
transferStatus = TransferStatus.TOO_FAR, transferStatus = TransferStatus.TOO_FAR,
); // We shouldn't be displaying the chipbar anyway
endItem = null,
) {
override fun getChipTextString(context: Context, otherDeviceName: String): Text {
// TODO(b/245610654): Better way to handle this.
throw IllegalArgumentException("FAR_FROM_RECEIVER should never be displayed, " +
"so its string should never be fetched")
}
};
/** /**
* Returns a fully-formed string with the text that the chip should display. * Returns a fully-formed string with the text that the chip should display.
* *
* Throws an NPE if [stringResId] is null.
*
* @param otherDeviceName the name of the other device involved in the transfer. * @param otherDeviceName the name of the other device involved in the transfer.
*/ */
fun getChipTextString(context: Context, otherDeviceName: String): String? { open fun getChipTextString(context: Context, otherDeviceName: String): Text {
if (stringResId == null) { return Text.Loaded(context.getString(stringResId!!, otherDeviceName))
return null
}
return context.getString(stringResId, otherDeviceName)
} }
/**
* Returns a click listener for the undo button on the chip. Returns null if this chip state
* doesn't have an undo button.
*
* @param chipbarCoordinator passed as a parameter in case we want to display a new chipbar
* when undo is clicked.
* @param undoCallback if present, the callback that should be called when the user clicks the
* undo button. The undo button will only be shown if this is non-null.
*/
open fun undoClickListener(
chipbarCoordinator: ChipbarCoordinator,
routeInfo: MediaRoute2Info,
undoCallback: IUndoMediaTransferCallback?,
uiEventLogger: MediaTttSenderUiEventLogger,
falsingManager: FalsingManager,
): View.OnClickListener? = null
companion object { companion object {
/** /**
* Returns the sender state enum associated with the given [displayState] from * Returns the sender state enum associated with the given [displayState] from
@@ -253,6 +200,26 @@ enum class ChipStateSender(
} }
} }
/** Represents the item that should be displayed in the end section of the chip. */
sealed class SenderEndItem {
/** A loading icon should be displayed. */
object Loading : SenderEndItem()
/** An error icon should be displayed. */
object Error : SenderEndItem()
/**
* An undo button should be displayed.
*
* @property uiEventOnClick the UI event to log when this button is clicked.
* @property newState the state that should immediately be transitioned to.
*/
data class UndoButton(
val uiEventOnClick: UiEventLogger.UiEventEnum,
@StatusBarManager.MediaTransferSenderState val newState: Int,
) : SenderEndItem()
}
// Give the Transfer*Triggered states a longer timeout since those states represent an active // 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 // process and we should keep the user informed about it as long as possible (but don't allow it to
// continue indefinitely). // continue indefinitely).

View File

@@ -20,14 +20,20 @@ import android.app.StatusBarManager
import android.content.Context import android.content.Context
import android.media.MediaRoute2Info import android.media.MediaRoute2Info
import android.util.Log import android.util.Log
import android.view.View
import com.android.internal.logging.UiEventLogger
import com.android.internal.statusbar.IUndoMediaTransferCallback import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.CoreStartable import com.android.systemui.CoreStartable
import com.android.systemui.R
import com.android.systemui.common.shared.model.Text
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.media.taptotransfer.MediaTttFlags import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.media.taptotransfer.common.MediaTttLogger import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.temporarydisplay.chipbar.ChipSenderInfo
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.temporarydisplay.chipbar.ChipbarEndItem
import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
import com.android.systemui.temporarydisplay.chipbar.SENDER_TAG import com.android.systemui.temporarydisplay.chipbar.SENDER_TAG
import javax.inject.Inject import javax.inject.Inject
@@ -107,7 +113,89 @@ constructor(
chipbarCoordinator.removeView(removalReason) chipbarCoordinator.removeView(removalReason)
} else { } else {
displayedState = chipState displayedState = chipState
chipbarCoordinator.displayView(ChipSenderInfo(chipState, routeInfo, undoCallback)) chipbarCoordinator.displayView(
createChipbarInfo(
chipState,
routeInfo,
undoCallback,
context,
logger,
)
)
} }
} }
/**
* Creates an instance of [ChipbarInfo] that can be sent to [ChipbarCoordinator] for display.
*/
private fun createChipbarInfo(
chipStateSender: ChipStateSender,
routeInfo: MediaRoute2Info,
undoCallback: IUndoMediaTransferCallback?,
context: Context,
logger: MediaTttLogger,
): ChipbarInfo {
val packageName = routeInfo.clientPackageName
val otherDeviceName = routeInfo.name.toString()
return ChipbarInfo(
// Display the app's icon as the start icon
startIcon = MediaTttUtils.getIconFromPackageName(context, packageName, logger),
text = chipStateSender.getChipTextString(context, otherDeviceName),
endItem =
when (chipStateSender.endItem) {
null -> null
is SenderEndItem.Loading -> ChipbarEndItem.Loading
is SenderEndItem.Error -> ChipbarEndItem.Error
is SenderEndItem.UndoButton -> {
if (undoCallback != null) {
getUndoButton(
undoCallback,
chipStateSender.endItem.uiEventOnClick,
chipStateSender.endItem.newState,
routeInfo,
)
} else {
null
}
}
}
)
}
/**
* Returns an undo button for the chip.
*
* When the button is clicked: [undoCallback] will be triggered, [uiEvent] will be logged, and
* this coordinator will transition to [newState].
*/
private fun getUndoButton(
undoCallback: IUndoMediaTransferCallback,
uiEvent: UiEventLogger.UiEventEnum,
@StatusBarManager.MediaTransferSenderState newState: Int,
routeInfo: MediaRoute2Info,
): ChipbarEndItem.Button {
val onClickListener =
View.OnClickListener {
uiEventLogger.logUndoClicked(uiEvent)
undoCallback.onUndoTriggered()
// The external service should eventually send us a new TransferTriggered state, but
// but that may take too long to go through the binder and the user may be confused
// as to why the UI hasn't changed yet. So, we immediately change the UI here.
updateMediaTapToTransferSenderDisplay(
newState,
routeInfo,
// Since we're force-updating the UI, we don't have any [undoCallback] from the
// external service (and TransferTriggered states don't have undo callbacks
// anyway).
undoCallback = null,
)
}
return ChipbarEndItem.Button(
Text.Resource(R.string.media_transfer_undo),
onClickListener,
)
}
} }

View File

@@ -18,7 +18,6 @@ package com.android.systemui.temporarydisplay.chipbar
import android.content.Context import android.content.Context
import android.graphics.Rect import android.graphics.Rect
import android.media.MediaRoute2Info
import android.os.PowerManager import android.os.PowerManager
import android.view.Gravity import android.view.Gravity
import android.view.MotionEvent import android.view.MotionEvent
@@ -27,25 +26,24 @@ import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager
import android.widget.TextView import android.widget.TextView
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.internal.widget.CachingIconView import com.android.internal.widget.CachingIconView
import com.android.systemui.Gefingerpoken import com.android.systemui.Gefingerpoken
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.animation.Interpolators import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ViewHierarchyAnimator import com.android.systemui.animation.ViewHierarchyAnimator
import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.common.ui.binder.TextViewBinder
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.common.MediaTttLogger import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.media.taptotransfer.common.MediaTttUtils import com.android.systemui.media.taptotransfer.common.MediaTttUtils
import com.android.systemui.media.taptotransfer.sender.ChipStateSender
import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger
import com.android.systemui.media.taptotransfer.sender.MediaTttSenderUiEventLogger
import com.android.systemui.media.taptotransfer.sender.TransferStatus
import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.view.ViewUtil import com.android.systemui.util.view.ViewUtil
import javax.inject.Inject import javax.inject.Inject
@@ -78,11 +76,10 @@ open class ChipbarCoordinator @Inject constructor(
accessibilityManager: AccessibilityManager, accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController, configurationController: ConfigurationController,
powerManager: PowerManager, powerManager: PowerManager,
private val uiEventLogger: MediaTttSenderUiEventLogger,
private val falsingManager: FalsingManager, private val falsingManager: FalsingManager,
private val falsingCollector: FalsingCollector, private val falsingCollector: FalsingCollector,
private val viewUtil: ViewUtil, private val viewUtil: ViewUtil,
) : TemporaryViewDisplayController<ChipSenderInfo, MediaTttLogger>( ) : TemporaryViewDisplayController<ChipbarInfo, MediaTttLogger>(
context, context,
logger, logger,
windowManager, windowManager,
@@ -104,15 +101,13 @@ open class ChipbarCoordinator @Inject constructor(
override fun start() {} override fun start() {}
override fun updateView( override fun updateView(
newInfo: ChipSenderInfo, newInfo: ChipbarInfo,
currentView: ViewGroup currentView: ViewGroup
) { ) {
// TODO(b/245610654): Adding logging here. // TODO(b/245610654): Adding logging here.
val chipState = newInfo.state
// Detect falsing touches on the chip. // Detect falsing touches on the chip.
parent = currentView.requireViewById(R.id.media_ttt_sender_chip) parent = currentView.requireViewById(R.id.chipbar_root_view)
parent.touchHandler = object : Gefingerpoken { parent.touchHandler = object : Gefingerpoken {
override fun onTouchEvent(ev: MotionEvent?): Boolean { override fun onTouchEvent(ev: MotionEvent?): Boolean {
falsingCollector.onTouchEvent(ev) falsingCollector.onTouchEvent(ev)
@@ -120,47 +115,49 @@ open class ChipbarCoordinator @Inject constructor(
} }
} }
// App icon // ---- Start icon ----
val iconInfo = MediaTttUtils.getIconInfoFromPackageName( val iconView = currentView.requireViewById<CachingIconView>(R.id.start_icon)
context, newInfo.routeInfo.clientPackageName, logger IconViewBinder.bind(newInfo.startIcon, iconView)
)
val iconView = currentView.requireViewById<CachingIconView>(R.id.app_icon)
iconView.setImageDrawable(iconInfo.drawable)
iconView.contentDescription = iconInfo.contentDescription
// Text // ---- Text ----
val otherDeviceName = newInfo.routeInfo.name.toString() val textView = currentView.requireViewById<TextView>(R.id.text)
val chipText = chipState.getChipTextString(context, otherDeviceName) TextViewBinder.bind(textView, newInfo.text)
currentView.requireViewById<TextView>(R.id.text).text = chipText
// ---- End item ----
// Loading // Loading
currentView.requireViewById<View>(R.id.loading).visibility = currentView.requireViewById<View>(R.id.loading).visibility =
(chipState.transferStatus == TransferStatus.IN_PROGRESS).visibleIfTrue() (newInfo.endItem == ChipbarEndItem.Loading).visibleIfTrue()
// Undo // Error
val undoView = currentView.requireViewById<View>(R.id.undo) currentView.requireViewById<View>(R.id.error).visibility =
val undoClickListener = chipState.undoClickListener( (newInfo.endItem == ChipbarEndItem.Error).visibleIfTrue()
this,
newInfo.routeInfo,
newInfo.undoCallback,
uiEventLogger,
falsingManager,
)
undoView.setOnClickListener(undoClickListener)
undoView.visibility = (undoClickListener != null).visibleIfTrue()
// Failure // Button
currentView.requireViewById<View>(R.id.failure_icon).visibility = val buttonView = currentView.requireViewById<TextView>(R.id.end_button)
(chipState.transferStatus == TransferStatus.FAILED).visibleIfTrue() if (newInfo.endItem is ChipbarEndItem.Button) {
TextViewBinder.bind(buttonView, newInfo.endItem.text)
// For accessibility val onClickListener = View.OnClickListener { clickedView ->
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@OnClickListener
newInfo.endItem.onClickListener.onClick(clickedView)
}
buttonView.setOnClickListener(onClickListener)
buttonView.visibility = View.VISIBLE
} else {
buttonView.visibility = View.GONE
}
// ---- Overall accessibility ----
currentView.requireViewById<ViewGroup>( currentView.requireViewById<ViewGroup>(
R.id.media_ttt_sender_chip_inner R.id.chipbar_inner
).contentDescription = "${iconInfo.contentDescription} $chipText" ).contentDescription =
"${newInfo.startIcon.contentDescription.loadContentDescription(context)} " +
"${newInfo.text.loadText(context)}"
} }
override fun animateViewIn(view: ViewGroup) { override fun animateViewIn(view: ViewGroup) {
val chipInnerView = view.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner) val chipInnerView = view.requireViewById<ViewGroup>(R.id.chipbar_inner)
ViewHierarchyAnimator.animateAddition( ViewHierarchyAnimator.animateAddition(
chipInnerView, chipInnerView,
ViewHierarchyAnimator.Hotspot.TOP, ViewHierarchyAnimator.Hotspot.TOP,
@@ -175,7 +172,7 @@ open class ChipbarCoordinator @Inject constructor(
override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) { override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
ViewHierarchyAnimator.animateRemoval( ViewHierarchyAnimator.animateRemoval(
view.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner), view.requireViewById<ViewGroup>(R.id.chipbar_inner),
ViewHierarchyAnimator.Hotspot.TOP, ViewHierarchyAnimator.Hotspot.TOP,
Interpolators.EMPHASIZED_ACCELERATE, Interpolators.EMPHASIZED_ACCELERATE,
ANIMATION_DURATION, ANIMATION_DURATION,
@@ -197,13 +194,5 @@ open class ChipbarCoordinator @Inject constructor(
} }
} }
data class ChipSenderInfo(
val state: ChipStateSender,
val routeInfo: MediaRoute2Info,
val undoCallback: IUndoMediaTransferCallback? = null
) : TemporaryViewInfo {
override fun getTimeoutMs() = state.timeout
}
const val SENDER_TAG = "MediaTapToTransferSender" const val SENDER_TAG = "MediaTapToTransferSender"
private const val ANIMATION_DURATION = 500L private const val ANIMATION_DURATION = 500L

View File

@@ -0,0 +1,53 @@
/*
* 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.temporarydisplay.chipbar
import android.view.View
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.temporarydisplay.TemporaryViewInfo
/**
* A container for all the state needed to display a chipbar via [ChipbarCoordinator].
*
* @property startIcon the icon to display at the start of the chipbar (on the left in LTR locales;
* on the right in RTL locales).
* @property text the text to display.
* @property endItem an optional end item to display at the end of the chipbar (on the right in LTR
* locales; on the left in RTL locales).
*/
data class ChipbarInfo(
val startIcon: Icon,
val text: Text,
val endItem: ChipbarEndItem?,
) : TemporaryViewInfo
/** The possible items to display at the end of the chipbar. */
sealed class ChipbarEndItem {
/** A loading icon should be displayed. */
object Loading : ChipbarEndItem()
/** An error icon should be displayed. */
object Error : ChipbarEndItem()
/**
* A button with the provided [text] and [onClickListener] functionality should be displayed.
*/
data class Button(val text: Text, val onClickListener: View.OnClickListener) : ChipbarEndItem()
// TODO(b/245610654): Add support for a generic icon.
}

View File

@@ -22,6 +22,9 @@ import android.graphics.drawable.Drawable
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import org.junit.Before import org.junit.Before
@@ -61,6 +64,34 @@ class MediaTttUtilsTest : SysuiTestCase() {
context.setMockPackageManager(packageManager) context.setMockPackageManager(packageManager)
} }
@Test
fun getIconFromPackageName_nullPackageName_returnsDefault() {
val icon = MediaTttUtils.getIconFromPackageName(context, appPackageName = null, logger)
val expectedDesc =
ContentDescription.Resource(R.string.media_output_dialog_unknown_launch_app_name)
.loadContentDescription(context)
assertThat(icon.contentDescription.loadContentDescription(context)).isEqualTo(expectedDesc)
}
@Test
fun getIconFromPackageName_invalidPackageName_returnsDefault() {
val icon = MediaTttUtils.getIconFromPackageName(context, "fakePackageName", logger)
val expectedDesc =
ContentDescription.Resource(R.string.media_output_dialog_unknown_launch_app_name)
.loadContentDescription(context)
assertThat(icon.contentDescription.loadContentDescription(context)).isEqualTo(expectedDesc)
}
@Test
fun getIconFromPackageName_validPackageName_returnsAppInfo() {
val icon = MediaTttUtils.getIconFromPackageName(context, PACKAGE_NAME, logger)
assertThat(icon)
.isEqualTo(Icon.Loaded(appIconFromPackageName, ContentDescription.Loaded(APP_NAME)))
}
@Test @Test
fun getIconInfoFromPackageName_nullPackageName_returnsDefault() { fun getIconInfoFromPackageName_nullPackageName_returnsDefault() {
val iconInfo = val iconInfo =

View File

@@ -17,6 +17,9 @@
package com.android.systemui.media.taptotransfer.sender package com.android.systemui.media.taptotransfer.sender
import android.app.StatusBarManager import android.app.StatusBarManager
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.media.MediaRoute2Info import android.media.MediaRoute2Info
import android.os.PowerManager import android.os.PowerManager
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
@@ -25,6 +28,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager
import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake import com.android.internal.logging.testing.UiEventLoggerFake
@@ -32,16 +36,17 @@ import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.media.taptotransfer.MediaTttFlags import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.media.taptotransfer.common.MediaTttLogger import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.temporarydisplay.chipbar.ChipSenderInfo
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.temporarydisplay.chipbar.FakeChipbarCoordinator import com.android.systemui.temporarydisplay.chipbar.FakeChipbarCoordinator
import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.view.ViewUtil import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
@@ -60,20 +65,29 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper @TestableLooper.RunWithLooper
class MediaTttSenderCoordinatorTest : SysuiTestCase() { class MediaTttSenderCoordinatorTest : SysuiTestCase() {
// Note: This tests are a bit like integration tests because they use a real instance of
// [ChipbarCoordinator] and verify that the coordinator displays the correct view, based on
// the inputs from [MediaTttSenderCoordinator].
private lateinit var underTest: MediaTttSenderCoordinator private lateinit var underTest: MediaTttSenderCoordinator
@Mock private lateinit var accessibilityManager: AccessibilityManager @Mock private lateinit var accessibilityManager: AccessibilityManager
@Mock private lateinit var applicationInfo: ApplicationInfo
@Mock private lateinit var commandQueue: CommandQueue @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var configurationController: ConfigurationController @Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var falsingManager: FalsingManager @Mock private lateinit var falsingManager: FalsingManager
@Mock private lateinit var falsingCollector: FalsingCollector @Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var logger: MediaTttLogger @Mock private lateinit var logger: MediaTttLogger
@Mock private lateinit var mediaTttFlags: MediaTttFlags @Mock private lateinit var mediaTttFlags: MediaTttFlags
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var powerManager: PowerManager @Mock private lateinit var powerManager: PowerManager
@Mock private lateinit var viewUtil: ViewUtil @Mock private lateinit var viewUtil: ViewUtil
@Mock private lateinit var windowManager: WindowManager @Mock private lateinit var windowManager: WindowManager
private lateinit var chipbarCoordinator: ChipbarCoordinator private lateinit var chipbarCoordinator: ChipbarCoordinator
private lateinit var commandQueueCallback: CommandQueue.Callbacks private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
private lateinit var fakeClock: FakeSystemClock private lateinit var fakeClock: FakeSystemClock
private lateinit var fakeExecutor: FakeExecutor private lateinit var fakeExecutor: FakeExecutor
private lateinit var uiEventLoggerFake: UiEventLoggerFake private lateinit var uiEventLoggerFake: UiEventLoggerFake
@@ -85,6 +99,18 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
whenever(mediaTttFlags.isMediaTttEnabled()).thenReturn(true) whenever(mediaTttFlags.isMediaTttEnabled()).thenReturn(true)
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenReturn(TIMEOUT) whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenReturn(TIMEOUT)
fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable)
whenever(
packageManager.getApplicationInfo(
eq(PACKAGE_NAME),
any<PackageManager.ApplicationInfoFlags>()
)
)
.thenReturn(applicationInfo)
context.setMockPackageManager(packageManager)
fakeClock = FakeSystemClock() fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock) fakeExecutor = FakeExecutor(fakeClock)
@@ -100,7 +126,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
accessibilityManager, accessibilityManager,
configurationController, configurationController,
powerManager, powerManager,
uiEventLogger,
falsingManager, falsingManager,
falsingCollector, falsingCollector,
viewUtil, viewUtil,
@@ -149,8 +174,15 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) val chipbarView = getChipbarView()
.isEqualTo(almostCloseToStartCast().state.getChipTextString(context, OTHER_DEVICE_NAME)) assertThat(chipbarView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipbarView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipbarView.getChipText())
.isEqualTo(ChipStateSender.ALMOST_CLOSE_TO_START_CAST.getExpectedStateText())
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.GONE)
assertThat(uiEventLoggerFake.eventId(0)) assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_START_CAST.id) .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_START_CAST.id)
} }
@@ -163,8 +195,15 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) val chipbarView = getChipbarView()
.isEqualTo(almostCloseToEndCast().state.getChipTextString(context, OTHER_DEVICE_NAME)) assertThat(chipbarView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipbarView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipbarView.getChipText())
.isEqualTo(ChipStateSender.ALMOST_CLOSE_TO_END_CAST.getExpectedStateText())
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.GONE)
assertThat(uiEventLoggerFake.eventId(0)) assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_END_CAST.id) .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_END_CAST.id)
} }
@@ -177,10 +216,15 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) val chipbarView = getChipbarView()
.isEqualTo( assertThat(chipbarView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
transferToReceiverTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME) assertThat(chipbarView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
) assertThat(chipbarView.getChipText())
.isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.getExpectedStateText())
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.GONE)
assertThat(uiEventLoggerFake.eventId(0)) assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_TRIGGERED.id) .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_TRIGGERED.id)
} }
@@ -193,10 +237,15 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) val chipbarView = getChipbarView()
.isEqualTo( assertThat(chipbarView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
transferToThisDeviceTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME) assertThat(chipbarView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
) assertThat(chipbarView.getChipText())
.isEqualTo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.getExpectedStateText())
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.GONE)
assertThat(uiEventLoggerFake.eventId(0)) assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_TRIGGERED.id) .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_TRIGGERED.id)
} }
@@ -209,14 +258,68 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) val chipbarView = getChipbarView()
.isEqualTo( assertThat(chipbarView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
transferToReceiverSucceeded().state.getChipTextString(context, OTHER_DEVICE_NAME) assertThat(chipbarView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
) assertThat(chipbarView.getChipText())
.isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED.getExpectedStateText())
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(uiEventLoggerFake.eventId(0)) assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_SUCCEEDED.id) .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_SUCCEEDED.id)
} }
@Test
fun transferToReceiverSucceeded_nullUndoCallback_noUndo() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
/* undoCallback= */ null
)
val chipbarView = getChipbarView()
assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.GONE)
}
@Test
fun transferToReceiverSucceeded_withUndoRunnable_undoVisible() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
/* undoCallback= */ object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
},
)
val chipbarView = getChipbarView()
assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
assertThat(chipbarView.getUndoButton().hasOnClickListeners()).isTrue()
}
@Test
fun transferToReceiverSucceeded_undoButtonClick_switchesToTransferToThisDeviceTriggered() {
var undoCallbackCalled = false
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
/* undoCallback= */ object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {
undoCallbackCalled = true
}
},
)
getChipbarView().getUndoButton().performClick()
// Event index 1 since initially displaying the succeeded chip would also log an event
assertThat(uiEventLoggerFake.eventId(1))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_RECEIVER_CLICKED.id)
assertThat(undoCallbackCalled).isTrue()
assertThat(getChipbarView().getChipText())
.isEqualTo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.getExpectedStateText())
}
@Test @Test
fun commandQueueCallback_transferToThisDeviceSucceeded_triggersCorrectChip() { fun commandQueueCallback_transferToThisDeviceSucceeded_triggersCorrectChip() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay( commandQueueCallback.updateMediaTapToTransferSenderDisplay(
@@ -225,14 +328,70 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) val chipbarView = getChipbarView()
.isEqualTo( assertThat(chipbarView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
transferToThisDeviceSucceeded().state.getChipTextString(context, OTHER_DEVICE_NAME) assertThat(chipbarView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
) assertThat(chipbarView.getChipText())
.isEqualTo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED.getExpectedStateText())
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(uiEventLoggerFake.eventId(0)) assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_SUCCEEDED.id) .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_SUCCEEDED.id)
} }
@Test
fun transferToThisDeviceSucceeded_nullUndoCallback_noUndo() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
/* undoCallback= */ null
)
val chipbarView = getChipbarView()
assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.GONE)
}
@Test
fun transferToThisDeviceSucceeded_withUndoRunnable_undoVisible() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
/* undoCallback= */ object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
},
)
val chipbarView = getChipbarView()
assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
assertThat(chipbarView.getUndoButton().hasOnClickListeners()).isTrue()
}
@Test
fun transferToThisDeviceSucceeded_undoButtonClick_switchesToTransferToThisDeviceTriggered() {
var undoCallbackCalled = false
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
/* undoCallback= */ object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {
undoCallbackCalled = true
}
},
)
getChipbarView().getUndoButton().performClick()
// Event index 1 since initially displaying the succeeded chip would also log an event
assertThat(uiEventLoggerFake.eventId(1))
.isEqualTo(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_THIS_DEVICE_CLICKED.id
)
assertThat(undoCallbackCalled).isTrue()
assertThat(getChipbarView().getChipText())
.isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.getExpectedStateText())
}
@Test @Test
fun commandQueueCallback_transferToReceiverFailed_triggersCorrectChip() { fun commandQueueCallback_transferToReceiverFailed_triggersCorrectChip() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay( commandQueueCallback.updateMediaTapToTransferSenderDisplay(
@@ -241,10 +400,15 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) val chipbarView = getChipbarView()
.isEqualTo( assertThat(chipbarView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
transferToReceiverFailed().state.getChipTextString(context, OTHER_DEVICE_NAME) assertThat(chipbarView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
) assertThat(chipbarView.getChipText())
.isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_FAILED.getExpectedStateText())
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.VISIBLE)
assertThat(uiEventLoggerFake.eventId(0)) assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_FAILED.id) .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_FAILED.id)
} }
@@ -257,10 +421,15 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) val chipbarView = getChipbarView()
.isEqualTo( assertThat(chipbarView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
transferToThisDeviceFailed().state.getChipTextString(context, OTHER_DEVICE_NAME) assertThat(chipbarView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
) assertThat(chipbarView.getChipText())
.isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_FAILED.getExpectedStateText())
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.VISIBLE)
assertThat(uiEventLoggerFake.eventId(0)) assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_FAILED.id) .isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_FAILED.id)
} }
@@ -407,53 +576,113 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
verify(windowManager).removeView(any()) verify(windowManager).removeView(any())
} }
private fun getChipView(): ViewGroup { @Test
fun transferToReceiverSucceeded_thenUndo_thenFar_viewStillDisplayedButDoesTimeOut() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
},
)
val chipbarView = getChipbarView()
assertThat(chipbarView.getChipText())
.isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED.getExpectedStateText())
// Because [MediaTttSenderCoordinator] internally creates the undo callback, we should
// verify that the new state it triggers operates just like any other state.
getChipbarView().getUndoButton().performClick()
fakeExecutor.runAllReady()
// Verify that the click updated us to the triggered state
assertThat(chipbarView.getChipText())
.isEqualTo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.getExpectedStateText())
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
null
)
fakeExecutor.runAllReady()
// Verify that we didn't remove the chipbar because it's in the triggered state
verify(windowManager, never()).removeView(any())
verify(logger).logRemovalBypass(any(), any())
fakeClock.advanceTime(TIMEOUT + 1L)
// Verify we eventually remove the chipbar
verify(windowManager).removeView(any())
}
@Test
fun transferToThisDeviceSucceeded_thenUndo_thenFar_viewStillDisplayedButDoesTimeOut() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
},
)
val chipbarView = getChipbarView()
assertThat(chipbarView.getChipText())
.isEqualTo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED.getExpectedStateText())
// Because [MediaTttSenderCoordinator] internally creates the undo callback, we should
// verify that the new state it triggers operates just like any other state.
getChipbarView().getUndoButton().performClick()
fakeExecutor.runAllReady()
// Verify that the click updated us to the triggered state
assertThat(chipbarView.getChipText())
.isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.getExpectedStateText())
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
null
)
fakeExecutor.runAllReady()
// Verify that we didn't remove the chipbar because it's in the triggered state
verify(windowManager, never()).removeView(any())
verify(logger).logRemovalBypass(any(), any())
fakeClock.advanceTime(TIMEOUT + 1L)
// Verify we eventually remove the chipbar
verify(windowManager).removeView(any())
}
private fun getChipbarView(): ViewGroup {
val viewCaptor = ArgumentCaptor.forClass(View::class.java) val viewCaptor = ArgumentCaptor.forClass(View::class.java)
verify(windowManager).addView(viewCaptor.capture(), any()) verify(windowManager).addView(viewCaptor.capture(), any())
return viewCaptor.value as ViewGroup return viewCaptor.value as ViewGroup
} }
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.start_icon)
private fun ViewGroup.getChipText(): String = private fun ViewGroup.getChipText(): String =
(this.requireViewById<TextView>(R.id.text)).text as String (this.requireViewById<TextView>(R.id.text)).text as String
/** Helper method providing default parameters to not clutter up the tests. */ private fun ViewGroup.getLoadingIcon(): View = this.requireViewById(R.id.loading)
private fun almostCloseToStartCast() =
ChipSenderInfo(ChipStateSender.ALMOST_CLOSE_TO_START_CAST, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */ private fun ViewGroup.getErrorIcon(): View = this.requireViewById(R.id.error)
private fun almostCloseToEndCast() =
ChipSenderInfo(ChipStateSender.ALMOST_CLOSE_TO_END_CAST, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */ private fun ViewGroup.getUndoButton(): View = this.requireViewById(R.id.end_button)
private fun transferToReceiverTriggered() =
ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */ private fun ChipStateSender.getExpectedStateText(): String? {
private fun transferToThisDeviceTriggered() = return this.getChipTextString(context, OTHER_DEVICE_NAME).loadText(context)
ChipSenderInfo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED, routeInfo) }
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToReceiverSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED, routeInfo, undoCallback)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToThisDeviceSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
ChipSenderInfo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED, routeInfo, undoCallback)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToReceiverFailed() =
ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_FAILED, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToThisDeviceFailed() =
ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_FAILED, routeInfo)
} }
private const val APP_NAME = "Fake app name"
private const val OTHER_DEVICE_NAME = "My Tablet" private const val OTHER_DEVICE_NAME = "My Tablet"
private const val PACKAGE_NAME = "com.android.systemui"
private const val TIMEOUT = 10000 private const val TIMEOUT = 10000
private val routeInfo = private val routeInfo =
MediaRoute2Info.Builder("id", OTHER_DEVICE_NAME) MediaRoute2Info.Builder("id", OTHER_DEVICE_NAME)
.addFeature("feature") .addFeature("feature")
.setClientPackageName("com.android.systemui") .setClientPackageName(PACKAGE_NAME)
.build() .build()

View File

@@ -22,7 +22,7 @@ import androidx.test.filters.SmallTest
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
@@ -125,19 +125,12 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase
} else { } else {
testCase.expected.contentDescription.invoke(context) testCase.expected.contentDescription.invoke(context)
} }
assertThat(iconFlow.value?.contentDescription?.getAsString()) assertThat(iconFlow.value?.contentDescription?.loadContentDescription(context))
.isEqualTo(expectedContentDescription) .isEqualTo(expectedContentDescription)
job.cancel() job.cancel()
} }
private fun ContentDescription.getAsString(): String? {
return when (this) {
is ContentDescription.Loaded -> this.description
is ContentDescription.Resource -> context.getString(this.res)
}
}
internal data class Expected( internal data class Expected(
/** The resource that should be used for the icon. */ /** The resource that should be used for the icon. */
@DrawableRes val iconResource: Int, @DrawableRes val iconResource: Int,

View File

@@ -16,10 +16,6 @@
package com.android.systemui.temporarydisplay.chipbar package com.android.systemui.temporarydisplay.chipbar
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.media.MediaRoute2Info
import android.os.PowerManager import android.os.PowerManager
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import android.testing.TestableLooper import android.testing.TestableLooper
@@ -31,19 +27,18 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.media.taptotransfer.common.MediaTttLogger import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.media.taptotransfer.sender.ChipStateSender
import com.android.systemui.media.taptotransfer.sender.MediaTttSenderUiEventLogger
import com.android.systemui.media.taptotransfer.sender.MediaTttSenderUiEvents
import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.view.ViewUtil import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
@@ -53,7 +48,6 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
@@ -64,437 +58,277 @@ import org.mockito.MockitoAnnotations
class ChipbarCoordinatorTest : SysuiTestCase() { class ChipbarCoordinatorTest : SysuiTestCase() {
private lateinit var underTest: FakeChipbarCoordinator private lateinit var underTest: FakeChipbarCoordinator
@Mock @Mock private lateinit var logger: MediaTttLogger
private lateinit var packageManager: PackageManager @Mock private lateinit var accessibilityManager: AccessibilityManager
@Mock @Mock private lateinit var configurationController: ConfigurationController
private lateinit var applicationInfo: ApplicationInfo @Mock private lateinit var powerManager: PowerManager
@Mock @Mock private lateinit var windowManager: WindowManager
private lateinit var logger: MediaTttLogger @Mock private lateinit var falsingManager: FalsingManager
@Mock @Mock private lateinit var falsingCollector: FalsingCollector
private lateinit var accessibilityManager: AccessibilityManager @Mock private lateinit var viewUtil: ViewUtil
@Mock
private lateinit var configurationController: ConfigurationController
@Mock
private lateinit var powerManager: PowerManager
@Mock
private lateinit var windowManager: WindowManager
@Mock
private lateinit var falsingManager: FalsingManager
@Mock
private lateinit var falsingCollector: FalsingCollector
@Mock
private lateinit var viewUtil: ViewUtil
private lateinit var fakeAppIconDrawable: Drawable
private lateinit var fakeClock: FakeSystemClock private lateinit var fakeClock: FakeSystemClock
private lateinit var fakeExecutor: FakeExecutor private lateinit var fakeExecutor: FakeExecutor
private lateinit var uiEventLoggerFake: UiEventLoggerFake private lateinit var uiEventLoggerFake: UiEventLoggerFake
private lateinit var senderUiEventLogger: MediaTttSenderUiEventLogger
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenReturn(TIMEOUT)
fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable)
whenever(packageManager.getApplicationInfo(
eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
)).thenReturn(applicationInfo)
context.setMockPackageManager(packageManager)
fakeClock = FakeSystemClock() fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock) fakeExecutor = FakeExecutor(fakeClock)
uiEventLoggerFake = UiEventLoggerFake() uiEventLoggerFake = UiEventLoggerFake()
senderUiEventLogger = MediaTttSenderUiEventLogger(uiEventLoggerFake)
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenReturn(TIMEOUT) underTest =
FakeChipbarCoordinator(
underTest = FakeChipbarCoordinator( context,
context, logger,
logger, windowManager,
windowManager, fakeExecutor,
fakeExecutor, accessibilityManager,
accessibilityManager, configurationController,
configurationController, powerManager,
powerManager, falsingManager,
senderUiEventLogger, falsingCollector,
falsingManager, viewUtil,
falsingCollector, )
viewUtil,
)
underTest.start() underTest.start()
} }
@Test @Test
fun almostCloseToStartCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() { fun displayView_loadedIcon_correctlyRendered() {
val state = almostCloseToStartCast() val drawable = context.getDrawable(R.drawable.ic_celebration)!!
underTest.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@Test
fun almostCloseToEndCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
val state = almostCloseToEndCast()
underTest.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@Test
fun transferToReceiverTriggered_appIcon_loadingIcon_noUndo_noFailureIcon() {
val state = transferToReceiverTriggered()
underTest.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@Test
fun transferToThisDeviceTriggered_appIcon_loadingIcon_noUndo_noFailureIcon() {
val state = transferToThisDeviceTriggered()
underTest.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@Test
fun transferToReceiverSucceeded_appIcon_deviceName_noLoadingIcon_noFailureIcon() {
val state = transferToReceiverSucceeded()
underTest.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@Test
fun transferToReceiverSucceeded_nullUndoRunnable_noUndo() {
underTest.displayView(transferToReceiverSucceeded(undoCallback = null))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
}
@Test
fun transferToReceiverSucceeded_withUndoRunnable_undoWithClick() {
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
underTest.displayView(transferToReceiverSucceeded(undoCallback))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().hasOnClickListeners()).isTrue()
}
@Test
fun transferToReceiverSucceeded_withUndoRunnable_undoButtonClickRunsRunnable() {
var undoCallbackCalled = false
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {
undoCallbackCalled = true
}
}
underTest.displayView(transferToReceiverSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
assertThat(undoCallbackCalled).isTrue()
}
@Test
fun transferToReceiverSucceeded_withUndoRunnable_falseTap_callbackNotRun() {
whenever(falsingManager.isFalseTap(anyInt())).thenReturn(true)
var undoCallbackCalled = false
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {
undoCallbackCalled = true
}
}
underTest.displayView(transferToReceiverSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
assertThat(undoCallbackCalled).isFalse()
}
@Test
fun transferToReceiverSucceeded_withUndoRunnable_realTap_callbackRun() {
whenever(falsingManager.isFalseTap(anyInt())).thenReturn(false)
var undoCallbackCalled = false
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {
undoCallbackCalled = true
}
}
underTest.displayView(transferToReceiverSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
assertThat(undoCallbackCalled).isTrue()
}
@Test
fun transferToReceiverSucceeded_undoButtonClick_switchesToTransferToThisDeviceTriggered() {
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
underTest.displayView(transferToReceiverSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
assertThat(getChipView().getChipText()).isEqualTo(
transferToThisDeviceTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_RECEIVER_CLICKED.id
)
}
@Test
fun transferToThisDeviceSucceeded_appIcon_deviceName_noLoadingIcon_noFailureIcon() {
val state = transferToThisDeviceSucceeded()
underTest.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@Test
fun transferToThisDeviceSucceeded_nullUndoRunnable_noUndo() {
underTest.displayView(transferToThisDeviceSucceeded(undoCallback = null))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
}
@Test
fun transferToThisDeviceSucceeded_withUndoRunnable_undoWithClick() {
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
underTest.displayView(transferToThisDeviceSucceeded(undoCallback))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().hasOnClickListeners()).isTrue()
}
@Test
fun transferToThisDeviceSucceeded_withUndoRunnable_undoButtonClickRunsRunnable() {
var undoCallbackCalled = false
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {
undoCallbackCalled = true
}
}
underTest.displayView(transferToThisDeviceSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
assertThat(undoCallbackCalled).isTrue()
}
@Test
fun transferToThisDeviceSucceeded_undoButtonClick_switchesToTransferToReceiverTriggered() {
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
underTest.displayView(transferToThisDeviceSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
assertThat(getChipView().getChipText()).isEqualTo(
transferToReceiverTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_THIS_DEVICE_CLICKED.id
)
}
@Test
fun transferToReceiverFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
val state = transferToReceiverFailed()
underTest.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(getChipView().getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.VISIBLE)
}
@Test
fun transferToThisDeviceFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
val state = transferToThisDeviceFailed()
underTest.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(getChipView().getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.VISIBLE)
}
@Test
fun changeFromAlmostCloseToStartToTransferTriggered_loadingIconAppears() {
underTest.displayView(almostCloseToStartCast())
underTest.displayView(transferToReceiverTriggered())
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
}
@Test
fun changeFromTransferTriggeredToTransferSucceeded_loadingIconDisappears() {
underTest.displayView(transferToReceiverTriggered())
underTest.displayView(transferToReceiverSucceeded())
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE)
}
@Test
fun changeFromTransferTriggeredToTransferSucceeded_undoButtonAppears() {
underTest.displayView(transferToReceiverTriggered())
underTest.displayView( underTest.displayView(
transferToReceiverSucceeded( ChipbarInfo(
object : IUndoMediaTransferCallback.Stub() { Icon.Loaded(drawable, contentDescription = ContentDescription.Loaded("loadedCD")),
override fun onUndoTriggered() {} Text.Loaded("text"),
} endItem = null,
) )
) )
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.VISIBLE) val iconView = getChipbarView().getStartIconView()
assertThat(iconView.drawable).isEqualTo(drawable)
assertThat(iconView.contentDescription).isEqualTo("loadedCD")
} }
@Test @Test
fun changeFromTransferSucceededToAlmostCloseToStart_undoButtonDisappears() { fun displayView_resourceIcon_correctlyRendered() {
underTest.displayView(transferToReceiverSucceeded()) val contentDescription = ContentDescription.Resource(R.string.controls_error_timeout)
underTest.displayView(almostCloseToStartCast()) underTest.displayView(
ChipbarInfo(
Icon.Resource(R.drawable.ic_cake, contentDescription),
Text.Loaded("text"),
endItem = null,
)
)
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE) val iconView = getChipbarView().getStartIconView()
assertThat(iconView.contentDescription)
.isEqualTo(contentDescription.loadContentDescription(context))
} }
@Test @Test
fun changeFromTransferTriggeredToTransferFailed_failureIconAppears() { fun displayView_loadedText_correctlyRendered() {
underTest.displayView(transferToReceiverTriggered()) underTest.displayView(
underTest.displayView(transferToReceiverFailed()) ChipbarInfo(
Icon.Resource(R.id.check_box, null),
Text.Loaded("display view text here"),
endItem = null,
)
)
assertThat(getChipView().getFailureIcon().visibility).isEqualTo(View.VISIBLE) assertThat(getChipbarView().getChipText()).isEqualTo("display view text here")
} }
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon) @Test
fun displayView_resourceText_correctlyRendered() {
underTest.displayView(
ChipbarInfo(
Icon.Resource(R.id.check_box, null),
Text.Resource(R.string.screenrecord_start_error),
endItem = null,
)
)
assertThat(getChipbarView().getChipText())
.isEqualTo(context.getString(R.string.screenrecord_start_error))
}
@Test
fun displayView_endItemNull_correctlyRendered() {
underTest.displayView(
ChipbarInfo(
Icon.Resource(R.id.check_box, null),
Text.Loaded("text"),
endItem = null,
)
)
val chipbarView = getChipbarView()
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getEndButton().visibility).isEqualTo(View.GONE)
}
@Test
fun displayView_endItemLoading_correctlyRendered() {
underTest.displayView(
ChipbarInfo(
Icon.Resource(R.id.check_box, null),
Text.Loaded("text"),
endItem = ChipbarEndItem.Loading,
)
)
val chipbarView = getChipbarView()
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getEndButton().visibility).isEqualTo(View.GONE)
}
@Test
fun displayView_endItemError_correctlyRendered() {
underTest.displayView(
ChipbarInfo(
Icon.Resource(R.id.check_box, null),
Text.Loaded("text"),
endItem = ChipbarEndItem.Error,
)
)
val chipbarView = getChipbarView()
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.VISIBLE)
assertThat(chipbarView.getEndButton().visibility).isEqualTo(View.GONE)
}
@Test
fun displayView_endItemButton_correctlyRendered() {
underTest.displayView(
ChipbarInfo(
Icon.Resource(R.id.check_box, null),
Text.Loaded("text"),
endItem =
ChipbarEndItem.Button(
Text.Loaded("button text"),
onClickListener = {},
),
)
)
val chipbarView = getChipbarView()
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getEndButton().visibility).isEqualTo(View.VISIBLE)
assertThat(chipbarView.getEndButton().text).isEqualTo("button text")
assertThat(chipbarView.getEndButton().hasOnClickListeners()).isTrue()
}
@Test
fun displayView_endItemButtonClicked_falseTap_listenerNotRun() {
whenever(falsingManager.isFalseTap(anyInt())).thenReturn(true)
var isClicked = false
val buttonClickListener = View.OnClickListener { isClicked = true }
underTest.displayView(
ChipbarInfo(
Icon.Resource(R.id.check_box, null),
Text.Loaded("text"),
endItem =
ChipbarEndItem.Button(
Text.Loaded("button text"),
buttonClickListener,
),
)
)
getChipbarView().getEndButton().performClick()
assertThat(isClicked).isFalse()
}
@Test
fun displayView_endItemButtonClicked_notFalseTap_listenerRun() {
whenever(falsingManager.isFalseTap(anyInt())).thenReturn(false)
var isClicked = false
val buttonClickListener = View.OnClickListener { isClicked = true }
underTest.displayView(
ChipbarInfo(
Icon.Resource(R.id.check_box, null),
Text.Loaded("text"),
endItem =
ChipbarEndItem.Button(
Text.Loaded("button text"),
buttonClickListener,
),
)
)
getChipbarView().getEndButton().performClick()
assertThat(isClicked).isTrue()
}
@Test
fun updateView_viewUpdated() {
// First, display a view
val drawable = context.getDrawable(R.drawable.ic_celebration)!!
underTest.displayView(
ChipbarInfo(
Icon.Loaded(drawable, contentDescription = ContentDescription.Loaded("loadedCD")),
Text.Loaded("title text"),
endItem = ChipbarEndItem.Loading,
)
)
val chipbarView = getChipbarView()
assertThat(chipbarView.getStartIconView().drawable).isEqualTo(drawable)
assertThat(chipbarView.getStartIconView().contentDescription).isEqualTo("loadedCD")
assertThat(chipbarView.getChipText()).isEqualTo("title text")
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getEndButton().visibility).isEqualTo(View.GONE)
// WHEN the view is updated
val newDrawable = context.getDrawable(R.drawable.ic_cake)!!
underTest.updateView(
ChipbarInfo(
Icon.Loaded(newDrawable, ContentDescription.Loaded("new CD")),
Text.Loaded("new title text"),
endItem = ChipbarEndItem.Error,
),
chipbarView
)
// THEN we display the new view
assertThat(chipbarView.getStartIconView().drawable).isEqualTo(newDrawable)
assertThat(chipbarView.getStartIconView().contentDescription).isEqualTo("new CD")
assertThat(chipbarView.getChipText()).isEqualTo("new title text")
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
assertThat(chipbarView.getErrorIcon().visibility).isEqualTo(View.VISIBLE)
assertThat(chipbarView.getEndButton().visibility).isEqualTo(View.GONE)
}
private fun ViewGroup.getStartIconView() = this.requireViewById<ImageView>(R.id.start_icon)
private fun ViewGroup.getChipText(): String = private fun ViewGroup.getChipText(): String =
(this.requireViewById<TextView>(R.id.text)).text as String (this.requireViewById<TextView>(R.id.text)).text as String
private fun ViewGroup.getLoadingIconVisibility(): Int = private fun ViewGroup.getLoadingIcon(): View = this.requireViewById(R.id.loading)
this.requireViewById<View>(R.id.loading).visibility
private fun ViewGroup.getUndoButton(): View = this.requireViewById(R.id.undo) private fun ViewGroup.getEndButton(): TextView = this.requireViewById(R.id.end_button)
private fun ViewGroup.getFailureIcon(): View = this.requireViewById(R.id.failure_icon) private fun ViewGroup.getErrorIcon(): View = this.requireViewById(R.id.error)
private fun getChipView(): ViewGroup { private fun getChipbarView(): ViewGroup {
val viewCaptor = ArgumentCaptor.forClass(View::class.java) val viewCaptor = ArgumentCaptor.forClass(View::class.java)
verify(windowManager).addView(viewCaptor.capture(), any()) verify(windowManager).addView(viewCaptor.capture(), any())
return viewCaptor.value as ViewGroup return viewCaptor.value as ViewGroup
} }
// TODO(b/245610654): For now, the below methods are duplicated between this test and
// [MediaTttSenderCoordinatorTest]. Once we define a generic API for [ChipbarCoordinator],
// these will no longer be duplicated.
/** Helper method providing default parameters to not clutter up the tests. */
private fun almostCloseToStartCast() =
ChipSenderInfo(ChipStateSender.ALMOST_CLOSE_TO_START_CAST, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */
private fun almostCloseToEndCast() =
ChipSenderInfo(ChipStateSender.ALMOST_CLOSE_TO_END_CAST, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToReceiverTriggered() =
ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToThisDeviceTriggered() =
ChipSenderInfo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToReceiverSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED, routeInfo, undoCallback)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToThisDeviceSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
ChipSenderInfo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED, routeInfo, undoCallback)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToReceiverFailed() =
ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_FAILED, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToThisDeviceFailed() =
ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_FAILED, routeInfo)
} }
private const val APP_NAME = "Fake app name"
private const val OTHER_DEVICE_NAME = "My Tablet"
private const val PACKAGE_NAME = "com.android.systemui"
private const val TIMEOUT = 10000 private const val TIMEOUT = 10000
private val routeInfo = MediaRoute2Info.Builder("id", OTHER_DEVICE_NAME)
.addFeature("feature")
.setClientPackageName(PACKAGE_NAME)
.build()

View File

@@ -24,7 +24,6 @@ import android.view.accessibility.AccessibilityManager
import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.media.taptotransfer.common.MediaTttLogger import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger
import com.android.systemui.media.taptotransfer.sender.MediaTttSenderUiEventLogger
import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.concurrency.DelayableExecutor
@@ -39,7 +38,6 @@ class FakeChipbarCoordinator(
accessibilityManager: AccessibilityManager, accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController, configurationController: ConfigurationController,
powerManager: PowerManager, powerManager: PowerManager,
uiEventLogger: MediaTttSenderUiEventLogger,
falsingManager: FalsingManager, falsingManager: FalsingManager,
falsingCollector: FalsingCollector, falsingCollector: FalsingCollector,
viewUtil: ViewUtil, viewUtil: ViewUtil,
@@ -52,7 +50,6 @@ class FakeChipbarCoordinator(
accessibilityManager, accessibilityManager,
configurationController, configurationController,
powerManager, powerManager,
uiEventLogger,
falsingManager, falsingManager,
falsingCollector, falsingCollector,
viewUtil, viewUtil,