Merge changes Ic5ef8de2,I9b43b526,Idce510fc into tm-qpr-dev
* changes: [Media TTT] Receiver ripple tweaks: Use an ellipse and use no sparkle strength (so that it's a smooth ripple not a turbulent one). [Chipbar] Cleanup: Group the display-related variables into one object. [Media TTT] Set specific touchable regions for the media chips so that other touches will get correctly passed through to the window below.
This commit is contained in:
committed by
Android (Google) Code Review
commit
8de3d142a5
@@ -19,6 +19,7 @@ package com.android.systemui.media.taptotransfer.receiver
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.StatusBarManager
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.Icon
|
||||
import android.media.MediaRoute2Info
|
||||
@@ -44,6 +45,7 @@ import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
|
||||
import com.android.systemui.temporarydisplay.TemporaryViewInfo
|
||||
import com.android.systemui.util.animation.AnimationUtil.Companion.frames
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import com.android.systemui.util.view.ViewUtil
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
@@ -63,6 +65,7 @@ class MediaTttChipControllerReceiver @Inject constructor(
|
||||
powerManager: PowerManager,
|
||||
@Main private val mainHandler: Handler,
|
||||
private val uiEventLogger: MediaTttReceiverUiEventLogger,
|
||||
private val viewUtil: ViewUtil,
|
||||
) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttLogger>(
|
||||
context,
|
||||
logger,
|
||||
@@ -83,7 +86,6 @@ class MediaTttChipControllerReceiver @Inject constructor(
|
||||
height = WindowManager.LayoutParams.MATCH_PARENT
|
||||
layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
|
||||
fitInsetsTypes = 0 // Ignore insets from all system bars
|
||||
flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
|
||||
}
|
||||
|
||||
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
|
||||
@@ -140,8 +142,6 @@ class MediaTttChipControllerReceiver @Inject constructor(
|
||||
}
|
||||
|
||||
override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
|
||||
super.updateView(newInfo, currentView)
|
||||
|
||||
val iconInfo = MediaTttUtils.getIconInfoFromPackageName(
|
||||
context, newInfo.routeInfo.clientPackageName, logger
|
||||
)
|
||||
@@ -154,14 +154,14 @@ class MediaTttChipControllerReceiver @Inject constructor(
|
||||
context.resources.getDimensionPixelSize(R.dimen.media_ttt_generic_icon_padding)
|
||||
}
|
||||
|
||||
val iconView = currentView.requireViewById<CachingIconView>(R.id.app_icon)
|
||||
val iconView = currentView.getAppIconView()
|
||||
iconView.setPadding(iconPadding, iconPadding, iconPadding, iconPadding)
|
||||
iconView.setImageDrawable(iconDrawable)
|
||||
iconView.contentDescription = iconContentDescription
|
||||
}
|
||||
|
||||
override fun animateViewIn(view: ViewGroup) {
|
||||
val appIconView = view.requireViewById<View>(R.id.app_icon)
|
||||
val appIconView = view.getAppIconView()
|
||||
appIconView.animate()
|
||||
.translationYBy(-1 * getTranslationAmount().toFloat())
|
||||
.setDuration(30.frames)
|
||||
@@ -175,6 +175,12 @@ class MediaTttChipControllerReceiver @Inject constructor(
|
||||
startRipple(view.requireViewById(R.id.ripple))
|
||||
}
|
||||
|
||||
override fun getTouchableRegion(view: View, outRect: Rect) {
|
||||
// Even though the app icon view isn't touchable, users might think it is. So, use it as the
|
||||
// touchable region to ensure that touches don't get passed to the window below.
|
||||
viewUtil.setRectToViewWindowLocation(view.getAppIconView(), outRect)
|
||||
}
|
||||
|
||||
/** Returns the amount that the chip will be translated by in its intro animation. */
|
||||
private fun getTranslationAmount(): Int {
|
||||
return context.resources.getDimensionPixelSize(R.dimen.media_ttt_receiver_vert_translation)
|
||||
@@ -202,16 +208,19 @@ class MediaTttChipControllerReceiver @Inject constructor(
|
||||
|
||||
private fun layoutRipple(rippleView: ReceiverChipRippleView) {
|
||||
val windowBounds = windowManager.currentWindowMetrics.bounds
|
||||
val height = windowBounds.height()
|
||||
val width = windowBounds.width()
|
||||
val height = windowBounds.height().toFloat()
|
||||
val width = windowBounds.width().toFloat()
|
||||
|
||||
val maxDiameter = height / 2.5f
|
||||
rippleView.setMaxSize(maxDiameter, maxDiameter)
|
||||
rippleView.setMaxSize(width / 2f, height / 2f)
|
||||
// Center the ripple on the bottom of the screen in the middle.
|
||||
rippleView.setCenter(width * 0.5f, height.toFloat())
|
||||
rippleView.setCenter(width * 0.5f, height)
|
||||
val color = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent)
|
||||
rippleView.setColor(color, 70)
|
||||
}
|
||||
|
||||
private fun View.getAppIconView(): CachingIconView {
|
||||
return this.requireViewById(R.id.app_icon)
|
||||
}
|
||||
}
|
||||
|
||||
data class ChipReceiverInfo(
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.systemui.media.taptotransfer.receiver
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import com.android.systemui.ripple.RippleShader
|
||||
import com.android.systemui.ripple.RippleView
|
||||
|
||||
/**
|
||||
@@ -25,9 +26,9 @@ import com.android.systemui.ripple.RippleView
|
||||
*/
|
||||
class ReceiverChipRippleView(context: Context?, attrs: AttributeSet?) : RippleView(context, attrs) {
|
||||
init {
|
||||
// TODO: use RippleShape#ELLIPSE when calling setupShader.
|
||||
setupShader()
|
||||
setupShader(RippleShader.RippleShape.ELLIPSE)
|
||||
setRippleFill(true)
|
||||
setSparkleStrength(0f)
|
||||
duration = 3000L
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.systemui.media.taptotransfer.sender
|
||||
|
||||
import android.app.StatusBarManager
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.media.MediaRoute2Info
|
||||
import android.os.PowerManager
|
||||
import android.util.Log
|
||||
@@ -46,6 +47,7 @@ import com.android.systemui.temporarydisplay.TemporaryDisplayRemovalReason
|
||||
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 dagger.Lazy
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -68,6 +70,7 @@ open class MediaTttChipControllerSender @Inject constructor(
|
||||
// And overcome performance issue, check [b/247817628] for details.
|
||||
private val falsingManager: Lazy<FalsingManager>,
|
||||
private val falsingCollector: Lazy<FalsingCollector>,
|
||||
private val viewUtil: ViewUtil,
|
||||
) : TemporaryViewDisplayController<ChipSenderInfo, MediaTttLogger>(
|
||||
context,
|
||||
logger,
|
||||
@@ -129,8 +132,6 @@ open class MediaTttChipControllerSender @Inject constructor(
|
||||
newInfo: ChipSenderInfo,
|
||||
currentView: ViewGroup
|
||||
) {
|
||||
super.updateView(newInfo, currentView)
|
||||
|
||||
val chipState = newInfo.state
|
||||
|
||||
// Detect falsing touches on the chip.
|
||||
@@ -207,10 +208,10 @@ open class MediaTttChipControllerSender @Inject constructor(
|
||||
// animateChipOut matches the animateChipIn.
|
||||
}
|
||||
|
||||
override fun shouldIgnoreViewRemoval(removalReason: String): Boolean {
|
||||
override fun shouldIgnoreViewRemoval(info: ChipSenderInfo, removalReason: String): Boolean {
|
||||
// Don't remove the chip if we're in progress or succeeded, since the user should still be
|
||||
// able to see the status of the transfer. (But do remove it if it's finally timed out.)
|
||||
val transferStatus = info?.state?.transferStatus
|
||||
val transferStatus = info.state.transferStatus
|
||||
if (
|
||||
(transferStatus == TransferStatus.IN_PROGRESS ||
|
||||
transferStatus == TransferStatus.SUCCEEDED) &&
|
||||
@@ -224,6 +225,10 @@ open class MediaTttChipControllerSender @Inject constructor(
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getTouchableRegion(view: View, outRect: Rect) {
|
||||
viewUtil.setRectToViewWindowLocation(view, outRect)
|
||||
}
|
||||
|
||||
private fun Boolean.visibleIfTrue(): Int {
|
||||
return if (this) {
|
||||
View.VISIBLE
|
||||
|
||||
@@ -20,17 +20,18 @@ import android.annotation.LayoutRes
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.PixelFormat
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.PowerManager
|
||||
import android.os.SystemClock
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.view.accessibility.AccessibilityManager
|
||||
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS
|
||||
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS
|
||||
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT
|
||||
import androidx.annotation.CallSuper
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
@@ -70,7 +71,8 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
||||
width = WindowManager.LayoutParams.WRAP_CONTENT
|
||||
height = WindowManager.LayoutParams.WRAP_CONTENT
|
||||
type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY
|
||||
flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
title = windowTitle
|
||||
format = PixelFormat.TRANSLUCENT
|
||||
setTrustedOverlay()
|
||||
@@ -84,11 +86,8 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
||||
*/
|
||||
internal abstract val windowLayoutParams: WindowManager.LayoutParams
|
||||
|
||||
/** The view currently being displayed. Null if the view is not being displayed. */
|
||||
private var view: ViewGroup? = null
|
||||
|
||||
/** The info currently being displayed. Null if the view is not being displayed. */
|
||||
internal var info: T? = null
|
||||
/** A container for all the display-related objects. Null if the view is not being displayed. */
|
||||
private var displayInfo: DisplayInfo? = null
|
||||
|
||||
/** A [Runnable] that, when run, will cancel the pending timeout of the view. */
|
||||
private var cancelViewTimeout: Runnable? = null
|
||||
@@ -100,10 +99,11 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
||||
* display the correct information in the view.
|
||||
*/
|
||||
fun displayView(newInfo: T) {
|
||||
val currentView = view
|
||||
val currentDisplayInfo = displayInfo
|
||||
|
||||
if (currentView != null) {
|
||||
updateView(newInfo, currentView)
|
||||
if (currentDisplayInfo != null) {
|
||||
currentDisplayInfo.info = newInfo
|
||||
updateView(currentDisplayInfo.info, currentDisplayInfo.view)
|
||||
} else {
|
||||
// The view is new, so set up all our callbacks and inflate the view
|
||||
configurationController.addCallback(displayScaleListener)
|
||||
@@ -140,19 +140,24 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
||||
val newView = LayoutInflater
|
||||
.from(context)
|
||||
.inflate(viewLayoutRes, null) as ViewGroup
|
||||
view = newView
|
||||
updateView(newInfo, newView)
|
||||
val newViewController = TouchableRegionViewController(newView, this::getTouchableRegion)
|
||||
newViewController.init()
|
||||
|
||||
// We don't need to hold on to the view controller since we never set anything additional
|
||||
// on it -- it will be automatically cleaned up when the view is detached.
|
||||
val newDisplayInfo = DisplayInfo(newView, newInfo)
|
||||
displayInfo = newDisplayInfo
|
||||
updateView(newDisplayInfo.info, newDisplayInfo.view)
|
||||
windowManager.addView(newView, windowLayoutParams)
|
||||
animateViewIn(newView)
|
||||
}
|
||||
|
||||
/** Removes then re-inflates the view. */
|
||||
private fun reinflateView() {
|
||||
val currentInfo = info
|
||||
if (view == null || currentInfo == null) { return }
|
||||
val currentViewInfo = displayInfo ?: return
|
||||
|
||||
windowManager.removeView(view)
|
||||
inflateAndUpdateView(currentInfo)
|
||||
windowManager.removeView(currentViewInfo.view)
|
||||
inflateAndUpdateView(currentViewInfo.info)
|
||||
}
|
||||
|
||||
private val displayScaleListener = object : ConfigurationController.ConfigurationListener {
|
||||
@@ -168,20 +173,20 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
||||
* change, etc.)
|
||||
*/
|
||||
fun removeView(removalReason: String) {
|
||||
if (shouldIgnoreViewRemoval(removalReason)) {
|
||||
val currentDisplayInfo = displayInfo ?: return
|
||||
if (shouldIgnoreViewRemoval(currentDisplayInfo.info, removalReason)) {
|
||||
return
|
||||
}
|
||||
val currentView = view ?: return
|
||||
|
||||
val currentView = currentDisplayInfo.view
|
||||
animateViewOut(currentView) { windowManager.removeView(currentView) }
|
||||
|
||||
logger.logChipRemoval(removalReason)
|
||||
configurationController.removeCallback(displayScaleListener)
|
||||
// Re-set the view to null immediately (instead as part of the animation end runnable) so
|
||||
// Re-set to null immediately (instead as part of the animation end runnable) so
|
||||
// that if a new view event comes in while this view is animating out, we still display the
|
||||
// new view appropriately.
|
||||
view = null
|
||||
info = null
|
||||
displayInfo = null
|
||||
// No need to time the view out since it's already gone
|
||||
cancelViewTimeout?.run()
|
||||
}
|
||||
@@ -191,15 +196,18 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
||||
*
|
||||
* Allows subclasses to keep the view visible for longer in certain circumstances.
|
||||
*/
|
||||
open fun shouldIgnoreViewRemoval(removalReason: String): Boolean = false
|
||||
open fun shouldIgnoreViewRemoval(info: T, removalReason: String): Boolean = false
|
||||
|
||||
/**
|
||||
* A method implemented by subclasses to update [currentView] based on [newInfo].
|
||||
*/
|
||||
@CallSuper
|
||||
open fun updateView(newInfo: T, currentView: ViewGroup) {
|
||||
info = newInfo
|
||||
}
|
||||
abstract fun updateView(newInfo: T, currentView: ViewGroup)
|
||||
|
||||
/**
|
||||
* Fills [outRect] with the touchable region of this view. This will be used by WindowManager
|
||||
* to decide which touch events go to the view.
|
||||
*/
|
||||
abstract fun getTouchableRegion(view: View, outRect: Rect)
|
||||
|
||||
/**
|
||||
* A method that can be implemented by subclasses to do custom animations for when the view
|
||||
@@ -216,6 +224,15 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
||||
internal open fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
|
||||
onAnimationEnd.run()
|
||||
}
|
||||
|
||||
/** A container for all the display-related state objects. */
|
||||
private inner class DisplayInfo(
|
||||
/** The view currently being displayed. */
|
||||
val view: ViewGroup,
|
||||
|
||||
/** The info currently being displayed. */
|
||||
var info: T,
|
||||
)
|
||||
}
|
||||
|
||||
object TemporaryDisplayRemovalReason {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import com.android.systemui.util.ViewController
|
||||
|
||||
/**
|
||||
* A view controller that will notify the [ViewTreeObserver] about the touchable region for this
|
||||
* view. This will be used by WindowManager to decide which touch events go to the view and which
|
||||
* pass through to the window below.
|
||||
*
|
||||
* @param touchableRegionSetter a function that, given the view and an out rect, fills the rect with
|
||||
* the touchable region of this view.
|
||||
*/
|
||||
class TouchableRegionViewController(
|
||||
view: View,
|
||||
touchableRegionSetter: (View, Rect) -> Unit,
|
||||
) : ViewController<View>(view) {
|
||||
|
||||
private val tempRect = Rect()
|
||||
|
||||
private val internalInsetsListener =
|
||||
ViewTreeObserver.OnComputeInternalInsetsListener { inoutInfo ->
|
||||
inoutInfo.setTouchableInsets(
|
||||
ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION
|
||||
)
|
||||
|
||||
tempRect.setEmpty()
|
||||
touchableRegionSetter.invoke(mView, tempRect)
|
||||
inoutInfo.touchableRegion.set(tempRect)
|
||||
}
|
||||
|
||||
public override fun onViewAttached() {
|
||||
mView.viewTreeObserver.addOnComputeInternalInsetsListener(internalInsetsListener)
|
||||
}
|
||||
|
||||
public override fun onViewDetached() {
|
||||
mView.viewTreeObserver.removeOnComputeInternalInsetsListener(internalInsetsListener)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,22 @@
|
||||
/*
|
||||
* 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.util.view
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import javax.inject.Inject
|
||||
@@ -23,4 +40,22 @@ class ViewUtil @Inject constructor() {
|
||||
top <= y &&
|
||||
y <= top + view.height
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets [outRect] to be the view's location within its window.
|
||||
*/
|
||||
fun setRectToViewWindowLocation(view: View, outRect: Rect) {
|
||||
val locInWindow = IntArray(2)
|
||||
view.getLocationInWindow(locInWindow)
|
||||
|
||||
val x = locInWindow[0]
|
||||
val y = locInWindow[1]
|
||||
|
||||
outRect.set(
|
||||
x,
|
||||
y,
|
||||
x + view.width,
|
||||
y + view.height,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ 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
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
@@ -71,6 +72,8 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
|
||||
@Mock
|
||||
private lateinit var powerManager: PowerManager
|
||||
@Mock
|
||||
private lateinit var viewUtil: ViewUtil
|
||||
@Mock
|
||||
private lateinit var windowManager: WindowManager
|
||||
@Mock
|
||||
private lateinit var commandQueue: CommandQueue
|
||||
@@ -104,7 +107,8 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
|
||||
configurationController,
|
||||
powerManager,
|
||||
Handler.getMain(),
|
||||
receiverUiEventLogger
|
||||
receiverUiEventLogger,
|
||||
viewUtil,
|
||||
)
|
||||
|
||||
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
|
||||
|
||||
@@ -47,6 +47,7 @@ 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
|
||||
import dagger.Lazy
|
||||
import org.junit.Before
|
||||
@@ -90,6 +91,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
private lateinit var lazyFalsingCollector: Lazy<FalsingCollector>
|
||||
@Mock
|
||||
private lateinit var falsingCollector: FalsingCollector
|
||||
@Mock
|
||||
private lateinit var viewUtil: ViewUtil
|
||||
private lateinit var commandQueueCallback: CommandQueue.Callbacks
|
||||
private lateinit var fakeAppIconDrawable: Drawable
|
||||
private lateinit var fakeClock: FakeSystemClock
|
||||
@@ -130,7 +133,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
powerManager,
|
||||
senderUiEventLogger,
|
||||
lazyFalsingManager,
|
||||
lazyFalsingCollector
|
||||
lazyFalsingCollector,
|
||||
viewUtil,
|
||||
)
|
||||
|
||||
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
|
||||
@@ -837,6 +841,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
uiEventLogger: MediaTttSenderUiEventLogger,
|
||||
falsingManager: Lazy<FalsingManager>,
|
||||
falsingCollector: Lazy<FalsingCollector>,
|
||||
viewUtil: ViewUtil,
|
||||
) : MediaTttChipControllerSender(
|
||||
commandQueue,
|
||||
context,
|
||||
@@ -849,6 +854,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
uiEventLogger,
|
||||
falsingManager,
|
||||
falsingCollector,
|
||||
viewUtil,
|
||||
) {
|
||||
override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
|
||||
// Just bypass the animation in tests
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
package com.android.systemui.temporarydisplay
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.os.PowerManager
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.view.accessibility.AccessibilityManager
|
||||
@@ -259,13 +261,16 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
||||
|
||||
override val windowLayoutParams = commonWindowLayoutParams
|
||||
override fun updateView(newInfo: ViewInfo, currentView: ViewGroup) {
|
||||
super.updateView(newInfo, currentView)
|
||||
mostRecentViewInfo = newInfo
|
||||
}
|
||||
|
||||
override fun shouldIgnoreViewRemoval(removalReason: String): Boolean {
|
||||
override fun shouldIgnoreViewRemoval(info: ViewInfo, removalReason: String): Boolean {
|
||||
return shouldIgnoreViewRemoval
|
||||
}
|
||||
|
||||
override fun getTouchableRegion(view: View, outRect: Rect) {
|
||||
outRect.setEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
inner class ViewInfo(val name: String) : TemporaryViewInfo {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
class TouchableRegionViewControllerTest : SysuiTestCase() {
|
||||
|
||||
@Mock private lateinit var view: View
|
||||
@Mock private lateinit var viewTreeObserver: ViewTreeObserver
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
whenever(view.viewTreeObserver).thenReturn(viewTreeObserver)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun viewAttached_listenerAdded() {
|
||||
val controller = TouchableRegionViewController(view) { _, _ -> }
|
||||
|
||||
controller.onViewAttached()
|
||||
|
||||
verify(viewTreeObserver).addOnComputeInternalInsetsListener(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun viewDetached_listenerRemoved() {
|
||||
val controller = TouchableRegionViewController(view) { _, _ -> }
|
||||
|
||||
controller.onViewDetached()
|
||||
|
||||
verify(viewTreeObserver).removeOnComputeInternalInsetsListener(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun listener_usesPassedInFunction() {
|
||||
val controller =
|
||||
TouchableRegionViewController(view) { _, outRect -> outRect.set(1, 2, 3, 4) }
|
||||
|
||||
controller.onViewAttached()
|
||||
|
||||
val captor =
|
||||
ArgumentCaptor.forClass(ViewTreeObserver.OnComputeInternalInsetsListener::class.java)
|
||||
verify(viewTreeObserver).addOnComputeInternalInsetsListener(captor.capture())
|
||||
val listener = captor.value!!
|
||||
|
||||
val inoutInfo = ViewTreeObserver.InternalInsetsInfo()
|
||||
listener.onComputeInternalInsets(inoutInfo)
|
||||
|
||||
assertThat(inoutInfo.touchableRegion.bounds).isEqualTo(Rect(1, 2, 3, 4))
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,31 @@
|
||||
/*
|
||||
* 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.util.view
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito.doAnswer
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.`when`
|
||||
|
||||
@@ -25,6 +44,12 @@ class ViewUtilTest : SysuiTestCase() {
|
||||
location[0] = VIEW_LEFT
|
||||
location[1] = VIEW_TOP
|
||||
`when`(view.locationOnScreen).thenReturn(location)
|
||||
doAnswer { invocation ->
|
||||
val pos = invocation.arguments[0] as IntArray
|
||||
pos[0] = VIEW_LEFT
|
||||
pos[1] = VIEW_TOP
|
||||
null
|
||||
}.`when`(view).getLocationInWindow(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -64,6 +89,18 @@ class ViewUtilTest : SysuiTestCase() {
|
||||
fun touchIsWithinView_yTooLarge_returnsFalse() {
|
||||
assertThat(viewUtil.touchIsWithinView(view, VIEW_LEFT + 1f, VIEW_BOTTOM + 1f)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setRectToViewWindowLocation_rectHasLocation() {
|
||||
val outRect = Rect()
|
||||
|
||||
viewUtil.setRectToViewWindowLocation(view, outRect)
|
||||
|
||||
assertThat(outRect.left).isEqualTo(VIEW_LEFT)
|
||||
assertThat(outRect.right).isEqualTo(VIEW_RIGHT)
|
||||
assertThat(outRect.top).isEqualTo(VIEW_TOP)
|
||||
assertThat(outRect.bottom).isEqualTo(VIEW_BOTTOM)
|
||||
}
|
||||
}
|
||||
|
||||
private const val VIEW_LEFT = 30
|
||||
|
||||
Reference in New Issue
Block a user