[Media TTT] Set specific touchable regions for the media chips so that
other touches will get correctly passed through to the window below. Fixes: 249639836 Fixes: 249632655 Test: manual: On lockscreen, display media sender chip then verify you can swipe down to open QS Test: manual: On homescreen, display media receiver chip then verify you can swipe up to see recent apps Test: manual: verify you can still click the Undo button on the "Transfer succeeded" chip Test: atest SystemUITests Change-Id: Idce510fc5b4e0c5ccd74d2b56e1d23db183ebe69
This commit is contained in:
@@ -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 {
|
||||
@@ -154,14 +156,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 +177,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)
|
||||
@@ -212,6 +220,10 @@ class MediaTttChipControllerReceiver @Inject constructor(
|
||||
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.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,
|
||||
@@ -224,6 +227,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,10 +20,12 @@ 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
|
||||
@@ -70,7 +72,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()
|
||||
@@ -87,9 +90,15 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
||||
/** The view currently being displayed. Null if the view is not being displayed. */
|
||||
private var view: ViewGroup? = null
|
||||
|
||||
/** The view controller for [view]. Null if the view is not being displayed. */
|
||||
private var viewController: TouchableRegionViewController? = null
|
||||
|
||||
/** The info currently being displayed. Null if the view is not being displayed. */
|
||||
internal var info: T? = null
|
||||
|
||||
// TODO(b/245610654): We should probably group [view], [viewController], and [info] together
|
||||
// into one object since they're either all null or all non-null.
|
||||
|
||||
/** A [Runnable] that, when run, will cancel the pending timeout of the view. */
|
||||
private var cancelViewTimeout: Runnable? = null
|
||||
|
||||
@@ -141,6 +150,11 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
||||
.from(context)
|
||||
.inflate(viewLayoutRes, null) as ViewGroup
|
||||
view = newView
|
||||
|
||||
val newViewController = TouchableRegionViewController(newView, this::getTouchableRegion)
|
||||
newViewController.init()
|
||||
viewController = newViewController
|
||||
|
||||
updateView(newInfo, newView)
|
||||
windowManager.addView(newView, windowLayoutParams)
|
||||
animateViewIn(newView)
|
||||
@@ -181,6 +195,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
||||
// that if a new view event comes in while this view is animating out, we still display the
|
||||
// new view appropriately.
|
||||
view = null
|
||||
viewController = null
|
||||
info = null
|
||||
// No need to time the view out since it's already gone
|
||||
cancelViewTimeout?.run()
|
||||
@@ -201,6 +216,12 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
||||
info = newInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* appears.
|
||||
|
||||
@@ -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
|
||||
@@ -266,6 +268,10 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
||||
override fun shouldIgnoreViewRemoval(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