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:
@@ -832,7 +832,6 @@
|
||||
-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/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/UnfoldLatencyTrackerTest.kt
|
||||
-packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
<com.android.systemui.temporarydisplay.chipbar.ChipbarRootView
|
||||
xmlns:android="http://schemas.android.com/apk/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_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/media_ttt_sender_chip_inner"
|
||||
android:id="@+id/chipbar_inner"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -39,7 +39,7 @@
|
||||
>
|
||||
|
||||
<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_height="@dimen/media_ttt_app_icon_size"
|
||||
android:layout_marginEnd="12dp"
|
||||
@@ -69,7 +69,7 @@
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/failure_icon"
|
||||
android:id="@+id/error"
|
||||
android:layout_width="@dimen/media_ttt_status_icon_size"
|
||||
android:layout_height="@dimen/media_ttt_status_icon_size"
|
||||
android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
|
||||
@@ -78,11 +78,11 @@
|
||||
android:alpha="0.0"
|
||||
/>
|
||||
|
||||
<!-- TODO(b/245610654): Re-name all the media-specific dimens to chipbar dimens instead. -->
|
||||
<TextView
|
||||
android:id="@+id/undo"
|
||||
android:id="@+id/end_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/media_transfer_undo"
|
||||
android:textColor="?androidprv:attr/textColorOnAccent"
|
||||
android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
|
||||
android:textSize="@dimen/media_ttt_text_size"
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.common.shared.model
|
||||
|
||||
import android.annotation.StringRes
|
||||
import android.content.Context
|
||||
|
||||
/**
|
||||
* Models a content description, that can either be already [loaded][ContentDescription.Loaded] or
|
||||
@@ -30,4 +31,20 @@ sealed class ContentDescription {
|
||||
data class Resource(
|
||||
@StringRes val res: Int,
|
||||
) : 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package com.android.systemui.common.shared.model
|
||||
|
||||
import android.annotation.StringRes
|
||||
import android.content.Context
|
||||
|
||||
/**
|
||||
* 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(
|
||||
@StringRes val res: Int,
|
||||
) : 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.android.settingslib.Utils
|
||||
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. */
|
||||
class MediaTttUtils {
|
||||
@@ -30,6 +32,23 @@ class MediaTttUtils {
|
||||
const val WINDOW_TITLE = "Media Transfer Chip View"
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -18,17 +18,12 @@ package com.android.systemui.media.taptotransfer.sender
|
||||
|
||||
import android.app.StatusBarManager
|
||||
import android.content.Context
|
||||
import android.media.MediaRoute2Info
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.annotation.StringRes
|
||||
import com.android.internal.logging.UiEventLogger
|
||||
import com.android.internal.statusbar.IUndoMediaTransferCallback
|
||||
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.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
|
||||
@@ -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
|
||||
* state should not have the chip be displayed.
|
||||
* @property transferStatus the transfer status that the chip state represents.
|
||||
* @property endItem the item that should be displayed in the end section of the chip.
|
||||
* @property timeout the amount of time this chip should display on the screen before it times out
|
||||
* and disappears.
|
||||
*/
|
||||
@@ -46,6 +42,7 @@ enum class ChipStateSender(
|
||||
val uiEvent: UiEventLogger.UiEventEnum,
|
||||
@StringRes val stringResId: Int?,
|
||||
val transferStatus: TransferStatus,
|
||||
val endItem: SenderEndItem?,
|
||||
val timeout: Long = DEFAULT_TIMEOUT_MILLIS
|
||||
) {
|
||||
/**
|
||||
@@ -58,6 +55,7 @@ enum class ChipStateSender(
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_START_CAST,
|
||||
R.string.media_move_closer_to_start_cast,
|
||||
transferStatus = TransferStatus.NOT_STARTED,
|
||||
endItem = null,
|
||||
),
|
||||
|
||||
/**
|
||||
@@ -71,6 +69,7 @@ enum class ChipStateSender(
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_END_CAST,
|
||||
R.string.media_move_closer_to_end_cast,
|
||||
transferStatus = TransferStatus.NOT_STARTED,
|
||||
endItem = null,
|
||||
),
|
||||
|
||||
/**
|
||||
@@ -82,6 +81,7 @@ enum class ChipStateSender(
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_TRIGGERED,
|
||||
R.string.media_transfer_playing_different_device,
|
||||
transferStatus = TransferStatus.IN_PROGRESS,
|
||||
endItem = SenderEndItem.Loading,
|
||||
timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
|
||||
),
|
||||
|
||||
@@ -94,6 +94,7 @@ enum class ChipStateSender(
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
|
||||
R.string.media_transfer_playing_this_device,
|
||||
transferStatus = TransferStatus.IN_PROGRESS,
|
||||
endItem = SenderEndItem.Loading,
|
||||
timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
|
||||
),
|
||||
|
||||
@@ -105,36 +106,13 @@ enum class ChipStateSender(
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_SUCCEEDED,
|
||||
R.string.media_transfer_playing_different_device,
|
||||
transferStatus = TransferStatus.SUCCEEDED,
|
||||
) {
|
||||
override fun undoClickListener(
|
||||
chipbarCoordinator: ChipbarCoordinator,
|
||||
routeInfo: MediaRoute2Info,
|
||||
undoCallback: IUndoMediaTransferCallback?,
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
endItem = SenderEndItem.UndoButton(
|
||||
uiEventOnClick =
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_RECEIVER_CLICKED,
|
||||
newState =
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED
|
||||
),
|
||||
),
|
||||
|
||||
/**
|
||||
* 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,
|
||||
R.string.media_transfer_playing_this_device,
|
||||
transferStatus = TransferStatus.SUCCEEDED,
|
||||
) {
|
||||
override fun undoClickListener(
|
||||
chipbarCoordinator: ChipbarCoordinator,
|
||||
routeInfo: MediaRoute2Info,
|
||||
undoCallback: IUndoMediaTransferCallback?,
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
endItem = SenderEndItem.UndoButton(
|
||||
uiEventOnClick =
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_THIS_DEVICE_CLICKED,
|
||||
newState =
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED
|
||||
),
|
||||
),
|
||||
|
||||
/** A state representing that a transfer to the receiver device has failed. */
|
||||
TRANSFER_TO_RECEIVER_FAILED(
|
||||
@@ -181,6 +136,7 @@ enum class ChipStateSender(
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_FAILED,
|
||||
R.string.media_transfer_failed,
|
||||
transferStatus = TransferStatus.FAILED,
|
||||
endItem = SenderEndItem.Error,
|
||||
),
|
||||
|
||||
/** 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,
|
||||
R.string.media_transfer_failed,
|
||||
transferStatus = TransferStatus.FAILED,
|
||||
endItem = SenderEndItem.Error,
|
||||
),
|
||||
|
||||
/** 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,
|
||||
stringResId = null,
|
||||
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.
|
||||
*
|
||||
* Throws an NPE if [stringResId] is null.
|
||||
*
|
||||
* @param otherDeviceName the name of the other device involved in the transfer.
|
||||
*/
|
||||
fun getChipTextString(context: Context, otherDeviceName: String): String? {
|
||||
if (stringResId == null) {
|
||||
return null
|
||||
}
|
||||
return context.getString(stringResId, otherDeviceName)
|
||||
open fun getChipTextString(context: Context, otherDeviceName: String): Text {
|
||||
return Text.Loaded(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 {
|
||||
/**
|
||||
* 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
|
||||
// process and we should keep the user informed about it as long as possible (but don't allow it to
|
||||
// continue indefinitely).
|
||||
|
||||
@@ -20,14 +20,20 @@ import android.app.StatusBarManager
|
||||
import android.content.Context
|
||||
import android.media.MediaRoute2Info
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import com.android.internal.logging.UiEventLogger
|
||||
import com.android.internal.statusbar.IUndoMediaTransferCallback
|
||||
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.media.taptotransfer.MediaTttFlags
|
||||
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.temporarydisplay.chipbar.ChipSenderInfo
|
||||
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 javax.inject.Inject
|
||||
|
||||
@@ -107,7 +113,89 @@ constructor(
|
||||
chipbarCoordinator.removeView(removalReason)
|
||||
} else {
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.android.systemui.temporarydisplay.chipbar
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.media.MediaRoute2Info
|
||||
import android.os.PowerManager
|
||||
import android.view.Gravity
|
||||
import android.view.MotionEvent
|
||||
@@ -27,25 +26,24 @@ import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.view.accessibility.AccessibilityManager
|
||||
import android.widget.TextView
|
||||
import com.android.internal.statusbar.IUndoMediaTransferCallback
|
||||
import com.android.internal.widget.CachingIconView
|
||||
import com.android.systemui.Gefingerpoken
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.animation.Interpolators
|
||||
import com.android.systemui.animation.ViewHierarchyAnimator
|
||||
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.qualifiers.Main
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
|
||||
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.MediaTttSenderUiEventLogger
|
||||
import com.android.systemui.media.taptotransfer.sender.TransferStatus
|
||||
import com.android.systemui.plugins.FalsingManager
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
|
||||
import com.android.systemui.temporarydisplay.TemporaryViewInfo
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import com.android.systemui.util.view.ViewUtil
|
||||
import javax.inject.Inject
|
||||
@@ -78,11 +76,10 @@ open class ChipbarCoordinator @Inject constructor(
|
||||
accessibilityManager: AccessibilityManager,
|
||||
configurationController: ConfigurationController,
|
||||
powerManager: PowerManager,
|
||||
private val uiEventLogger: MediaTttSenderUiEventLogger,
|
||||
private val falsingManager: FalsingManager,
|
||||
private val falsingCollector: FalsingCollector,
|
||||
private val viewUtil: ViewUtil,
|
||||
) : TemporaryViewDisplayController<ChipSenderInfo, MediaTttLogger>(
|
||||
) : TemporaryViewDisplayController<ChipbarInfo, MediaTttLogger>(
|
||||
context,
|
||||
logger,
|
||||
windowManager,
|
||||
@@ -104,15 +101,13 @@ open class ChipbarCoordinator @Inject constructor(
|
||||
override fun start() {}
|
||||
|
||||
override fun updateView(
|
||||
newInfo: ChipSenderInfo,
|
||||
newInfo: ChipbarInfo,
|
||||
currentView: ViewGroup
|
||||
) {
|
||||
// TODO(b/245610654): Adding logging here.
|
||||
|
||||
val chipState = newInfo.state
|
||||
|
||||
// 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 {
|
||||
override fun onTouchEvent(ev: MotionEvent?): Boolean {
|
||||
falsingCollector.onTouchEvent(ev)
|
||||
@@ -120,47 +115,49 @@ open class ChipbarCoordinator @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
// App icon
|
||||
val iconInfo = MediaTttUtils.getIconInfoFromPackageName(
|
||||
context, newInfo.routeInfo.clientPackageName, logger
|
||||
)
|
||||
val iconView = currentView.requireViewById<CachingIconView>(R.id.app_icon)
|
||||
iconView.setImageDrawable(iconInfo.drawable)
|
||||
iconView.contentDescription = iconInfo.contentDescription
|
||||
// ---- Start icon ----
|
||||
val iconView = currentView.requireViewById<CachingIconView>(R.id.start_icon)
|
||||
IconViewBinder.bind(newInfo.startIcon, iconView)
|
||||
|
||||
// Text
|
||||
val otherDeviceName = newInfo.routeInfo.name.toString()
|
||||
val chipText = chipState.getChipTextString(context, otherDeviceName)
|
||||
currentView.requireViewById<TextView>(R.id.text).text = chipText
|
||||
// ---- Text ----
|
||||
val textView = currentView.requireViewById<TextView>(R.id.text)
|
||||
TextViewBinder.bind(textView, newInfo.text)
|
||||
|
||||
// ---- End item ----
|
||||
// Loading
|
||||
currentView.requireViewById<View>(R.id.loading).visibility =
|
||||
(chipState.transferStatus == TransferStatus.IN_PROGRESS).visibleIfTrue()
|
||||
(newInfo.endItem == ChipbarEndItem.Loading).visibleIfTrue()
|
||||
|
||||
// Undo
|
||||
val undoView = currentView.requireViewById<View>(R.id.undo)
|
||||
val undoClickListener = chipState.undoClickListener(
|
||||
this,
|
||||
newInfo.routeInfo,
|
||||
newInfo.undoCallback,
|
||||
uiEventLogger,
|
||||
falsingManager,
|
||||
)
|
||||
undoView.setOnClickListener(undoClickListener)
|
||||
undoView.visibility = (undoClickListener != null).visibleIfTrue()
|
||||
// Error
|
||||
currentView.requireViewById<View>(R.id.error).visibility =
|
||||
(newInfo.endItem == ChipbarEndItem.Error).visibleIfTrue()
|
||||
|
||||
// Failure
|
||||
currentView.requireViewById<View>(R.id.failure_icon).visibility =
|
||||
(chipState.transferStatus == TransferStatus.FAILED).visibleIfTrue()
|
||||
// Button
|
||||
val buttonView = currentView.requireViewById<TextView>(R.id.end_button)
|
||||
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>(
|
||||
R.id.media_ttt_sender_chip_inner
|
||||
).contentDescription = "${iconInfo.contentDescription} $chipText"
|
||||
R.id.chipbar_inner
|
||||
).contentDescription =
|
||||
"${newInfo.startIcon.contentDescription.loadContentDescription(context)} " +
|
||||
"${newInfo.text.loadText(context)}"
|
||||
}
|
||||
|
||||
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(
|
||||
chipInnerView,
|
||||
ViewHierarchyAnimator.Hotspot.TOP,
|
||||
@@ -175,7 +172,7 @@ open class ChipbarCoordinator @Inject constructor(
|
||||
|
||||
override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
|
||||
ViewHierarchyAnimator.animateRemoval(
|
||||
view.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner),
|
||||
view.requireViewById<ViewGroup>(R.id.chipbar_inner),
|
||||
ViewHierarchyAnimator.Hotspot.TOP,
|
||||
Interpolators.EMPHASIZED_ACCELERATE,
|
||||
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"
|
||||
private const val ANIMATION_DURATION = 500L
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
@@ -22,6 +22,9 @@ import android.graphics.drawable.Drawable
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
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.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
@@ -61,6 +64,34 @@ class MediaTttUtilsTest : SysuiTestCase() {
|
||||
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
|
||||
fun getIconInfoFromPackageName_nullPackageName_returnsDefault() {
|
||||
val iconInfo =
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
package com.android.systemui.media.taptotransfer.sender
|
||||
|
||||
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.os.PowerManager
|
||||
import android.testing.AndroidTestingRunner
|
||||
@@ -25,6 +28,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.view.accessibility.AccessibilityManager
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.test.filters.SmallTest
|
||||
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.SysuiTestCase
|
||||
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.common.MediaTttLogger
|
||||
import com.android.systemui.plugins.FalsingManager
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
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.FakeChipbarCoordinator
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
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.view.ViewUtil
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
@@ -60,20 +65,29 @@ import org.mockito.MockitoAnnotations
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
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
|
||||
|
||||
@Mock private lateinit var accessibilityManager: AccessibilityManager
|
||||
@Mock private lateinit var applicationInfo: ApplicationInfo
|
||||
@Mock private lateinit var commandQueue: CommandQueue
|
||||
@Mock private lateinit var configurationController: ConfigurationController
|
||||
@Mock private lateinit var falsingManager: FalsingManager
|
||||
@Mock private lateinit var falsingCollector: FalsingCollector
|
||||
@Mock private lateinit var logger: MediaTttLogger
|
||||
@Mock private lateinit var mediaTttFlags: MediaTttFlags
|
||||
@Mock private lateinit var packageManager: PackageManager
|
||||
|
||||
@Mock private lateinit var powerManager: PowerManager
|
||||
@Mock private lateinit var viewUtil: ViewUtil
|
||||
@Mock private lateinit var windowManager: WindowManager
|
||||
private lateinit var chipbarCoordinator: ChipbarCoordinator
|
||||
private lateinit var commandQueueCallback: CommandQueue.Callbacks
|
||||
private lateinit var fakeAppIconDrawable: Drawable
|
||||
private lateinit var fakeClock: FakeSystemClock
|
||||
private lateinit var fakeExecutor: FakeExecutor
|
||||
private lateinit var uiEventLoggerFake: UiEventLoggerFake
|
||||
@@ -85,6 +99,18 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
||||
whenever(mediaTttFlags.isMediaTttEnabled()).thenReturn(true)
|
||||
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()
|
||||
fakeExecutor = FakeExecutor(fakeClock)
|
||||
|
||||
@@ -100,7 +126,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
||||
accessibilityManager,
|
||||
configurationController,
|
||||
powerManager,
|
||||
uiEventLogger,
|
||||
falsingManager,
|
||||
falsingCollector,
|
||||
viewUtil,
|
||||
@@ -149,8 +174,15 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(almostCloseToStartCast().state.getChipTextString(context, OTHER_DEVICE_NAME))
|
||||
val chipbarView = getChipbarView()
|
||||
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))
|
||||
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_START_CAST.id)
|
||||
}
|
||||
@@ -163,8 +195,15 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(almostCloseToEndCast().state.getChipTextString(context, OTHER_DEVICE_NAME))
|
||||
val chipbarView = getChipbarView()
|
||||
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))
|
||||
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_END_CAST.id)
|
||||
}
|
||||
@@ -177,10 +216,15 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(
|
||||
transferToReceiverTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
val chipbarView = getChipbarView()
|
||||
assertThat(chipbarView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
|
||||
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))
|
||||
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_TRIGGERED.id)
|
||||
}
|
||||
@@ -193,10 +237,15 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(
|
||||
transferToThisDeviceTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
val chipbarView = getChipbarView()
|
||||
assertThat(chipbarView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
|
||||
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))
|
||||
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_TRIGGERED.id)
|
||||
}
|
||||
@@ -209,14 +258,68 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(
|
||||
transferToReceiverSucceeded().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
val chipbarView = getChipbarView()
|
||||
assertThat(chipbarView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
|
||||
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))
|
||||
.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
|
||||
fun commandQueueCallback_transferToThisDeviceSucceeded_triggersCorrectChip() {
|
||||
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||
@@ -225,14 +328,70 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(
|
||||
transferToThisDeviceSucceeded().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
val chipbarView = getChipbarView()
|
||||
assertThat(chipbarView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
|
||||
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))
|
||||
.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
|
||||
fun commandQueueCallback_transferToReceiverFailed_triggersCorrectChip() {
|
||||
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||
@@ -241,10 +400,15 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(
|
||||
transferToReceiverFailed().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
val chipbarView = getChipbarView()
|
||||
assertThat(chipbarView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
|
||||
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))
|
||||
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_FAILED.id)
|
||||
}
|
||||
@@ -257,10 +421,15 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(
|
||||
transferToThisDeviceFailed().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
val chipbarView = getChipbarView()
|
||||
assertThat(chipbarView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
|
||||
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))
|
||||
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_FAILED.id)
|
||||
}
|
||||
@@ -407,53 +576,113 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
||||
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)
|
||||
verify(windowManager).addView(viewCaptor.capture(), any())
|
||||
return viewCaptor.value as ViewGroup
|
||||
}
|
||||
|
||||
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.start_icon)
|
||||
|
||||
private fun ViewGroup.getChipText(): String =
|
||||
(this.requireViewById<TextView>(R.id.text)).text as String
|
||||
|
||||
/** Helper method providing default parameters to not clutter up the tests. */
|
||||
private fun almostCloseToStartCast() =
|
||||
ChipSenderInfo(ChipStateSender.ALMOST_CLOSE_TO_START_CAST, routeInfo)
|
||||
private fun ViewGroup.getLoadingIcon(): View = this.requireViewById(R.id.loading)
|
||||
|
||||
/** Helper method providing default parameters to not clutter up the tests. */
|
||||
private fun almostCloseToEndCast() =
|
||||
ChipSenderInfo(ChipStateSender.ALMOST_CLOSE_TO_END_CAST, routeInfo)
|
||||
private fun ViewGroup.getErrorIcon(): View = this.requireViewById(R.id.error)
|
||||
|
||||
/** Helper method providing default parameters to not clutter up the tests. */
|
||||
private fun transferToReceiverTriggered() =
|
||||
ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED, routeInfo)
|
||||
private fun ViewGroup.getUndoButton(): View = this.requireViewById(R.id.end_button)
|
||||
|
||||
/** 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 fun ChipStateSender.getExpectedStateText(): String? {
|
||||
return this.getChipTextString(context, OTHER_DEVICE_NAME).loadText(context)
|
||||
}
|
||||
}
|
||||
|
||||
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 val routeInfo =
|
||||
MediaRoute2Info.Builder("id", OTHER_DEVICE_NAME)
|
||||
.addFeature("feature")
|
||||
.setClientPackageName("com.android.systemui")
|
||||
.setClientPackageName(PACKAGE_NAME)
|
||||
.build()
|
||||
|
||||
@@ -22,7 +22,7 @@ import androidx.test.filters.SmallTest
|
||||
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
|
||||
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
|
||||
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_NO_INTERNET_ICONS
|
||||
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
|
||||
@@ -125,19 +125,12 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase
|
||||
} else {
|
||||
testCase.expected.contentDescription.invoke(context)
|
||||
}
|
||||
assertThat(iconFlow.value?.contentDescription?.getAsString())
|
||||
assertThat(iconFlow.value?.contentDescription?.loadContentDescription(context))
|
||||
.isEqualTo(expectedContentDescription)
|
||||
|
||||
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(
|
||||
/** The resource that should be used for the icon. */
|
||||
@DrawableRes val iconResource: Int,
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
|
||||
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.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
@@ -31,19 +27,18 @@ import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.logging.testing.UiEventLoggerFake
|
||||
import com.android.internal.statusbar.IUndoMediaTransferCallback
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
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.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.statusbar.policy.ConfigurationController
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
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.view.ViewUtil
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
@@ -53,7 +48,6 @@ import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
@@ -64,437 +58,277 @@ import org.mockito.MockitoAnnotations
|
||||
class ChipbarCoordinatorTest : SysuiTestCase() {
|
||||
private lateinit var underTest: FakeChipbarCoordinator
|
||||
|
||||
@Mock
|
||||
private lateinit var packageManager: PackageManager
|
||||
@Mock
|
||||
private lateinit var applicationInfo: ApplicationInfo
|
||||
@Mock
|
||||
private lateinit var logger: MediaTttLogger
|
||||
@Mock
|
||||
private lateinit var accessibilityManager: AccessibilityManager
|
||||
@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
|
||||
@Mock private lateinit var logger: MediaTttLogger
|
||||
@Mock private lateinit var accessibilityManager: AccessibilityManager
|
||||
@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 fakeClock: FakeSystemClock
|
||||
private lateinit var fakeExecutor: FakeExecutor
|
||||
private lateinit var uiEventLoggerFake: UiEventLoggerFake
|
||||
private lateinit var senderUiEventLogger: MediaTttSenderUiEventLogger
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
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)
|
||||
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenReturn(TIMEOUT)
|
||||
|
||||
fakeClock = FakeSystemClock()
|
||||
fakeExecutor = FakeExecutor(fakeClock)
|
||||
|
||||
uiEventLoggerFake = UiEventLoggerFake()
|
||||
senderUiEventLogger = MediaTttSenderUiEventLogger(uiEventLoggerFake)
|
||||
|
||||
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenReturn(TIMEOUT)
|
||||
|
||||
underTest = FakeChipbarCoordinator(
|
||||
context,
|
||||
logger,
|
||||
windowManager,
|
||||
fakeExecutor,
|
||||
accessibilityManager,
|
||||
configurationController,
|
||||
powerManager,
|
||||
senderUiEventLogger,
|
||||
falsingManager,
|
||||
falsingCollector,
|
||||
viewUtil,
|
||||
)
|
||||
underTest =
|
||||
FakeChipbarCoordinator(
|
||||
context,
|
||||
logger,
|
||||
windowManager,
|
||||
fakeExecutor,
|
||||
accessibilityManager,
|
||||
configurationController,
|
||||
powerManager,
|
||||
falsingManager,
|
||||
falsingCollector,
|
||||
viewUtil,
|
||||
)
|
||||
underTest.start()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun almostCloseToStartCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
|
||||
val state = almostCloseToStartCast()
|
||||
underTest.displayView(state)
|
||||
fun displayView_loadedIcon_correctlyRendered() {
|
||||
val drawable = context.getDrawable(R.drawable.ic_celebration)!!
|
||||
|
||||
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(
|
||||
transferToReceiverSucceeded(
|
||||
object : IUndoMediaTransferCallback.Stub() {
|
||||
override fun onUndoTriggered() {}
|
||||
}
|
||||
ChipbarInfo(
|
||||
Icon.Loaded(drawable, contentDescription = ContentDescription.Loaded("loadedCD")),
|
||||
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
|
||||
fun changeFromTransferSucceededToAlmostCloseToStart_undoButtonDisappears() {
|
||||
underTest.displayView(transferToReceiverSucceeded())
|
||||
underTest.displayView(almostCloseToStartCast())
|
||||
fun displayView_resourceIcon_correctlyRendered() {
|
||||
val contentDescription = ContentDescription.Resource(R.string.controls_error_timeout)
|
||||
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
|
||||
fun changeFromTransferTriggeredToTransferFailed_failureIconAppears() {
|
||||
underTest.displayView(transferToReceiverTriggered())
|
||||
underTest.displayView(transferToReceiverFailed())
|
||||
fun displayView_loadedText_correctlyRendered() {
|
||||
underTest.displayView(
|
||||
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 =
|
||||
(this.requireViewById<TextView>(R.id.text)).text as String
|
||||
|
||||
private fun ViewGroup.getLoadingIconVisibility(): Int =
|
||||
this.requireViewById<View>(R.id.loading).visibility
|
||||
private fun ViewGroup.getLoadingIcon(): View = this.requireViewById(R.id.loading)
|
||||
|
||||
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)
|
||||
verify(windowManager).addView(viewCaptor.capture(), any())
|
||||
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 val routeInfo = MediaRoute2Info.Builder("id", OTHER_DEVICE_NAME)
|
||||
.addFeature("feature")
|
||||
.setClientPackageName(PACKAGE_NAME)
|
||||
.build()
|
||||
|
||||
@@ -24,7 +24,6 @@ import android.view.accessibility.AccessibilityManager
|
||||
import com.android.systemui.classifier.FalsingCollector
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
|
||||
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.statusbar.policy.ConfigurationController
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
@@ -39,7 +38,6 @@ class FakeChipbarCoordinator(
|
||||
accessibilityManager: AccessibilityManager,
|
||||
configurationController: ConfigurationController,
|
||||
powerManager: PowerManager,
|
||||
uiEventLogger: MediaTttSenderUiEventLogger,
|
||||
falsingManager: FalsingManager,
|
||||
falsingCollector: FalsingCollector,
|
||||
viewUtil: ViewUtil,
|
||||
@@ -52,7 +50,6 @@ class FakeChipbarCoordinator(
|
||||
accessibilityManager,
|
||||
configurationController,
|
||||
powerManager,
|
||||
uiEventLogger,
|
||||
falsingManager,
|
||||
falsingCollector,
|
||||
viewUtil,
|
||||
|
||||
Reference in New Issue
Block a user