[go: nahoru, domu]

blob: 15f5c6ae940667fd211f0b79817c4c8b8efd72be [file] [log] [blame]
/*
* Copyright 2020 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 androidx.ui.node
import android.content.Context
import android.view.MotionEvent
import android.view.MotionEvent.ACTION_DOWN
import android.view.MotionEvent.ACTION_UP
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.compose.Recomposer
import androidx.compose.foundation.Box
import androidx.test.filters.MediumTest
import androidx.ui.core.DensityAmbient
import androidx.ui.core.setContent
import androidx.ui.framework.test.TestActivity
import androidx.ui.test.android.AndroidComposeTestRule
import androidx.ui.unit.dp
import androidx.ui.viewinterop.AndroidView
import com.nhaarman.mockitokotlin2.clearInvocations
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.never
import com.nhaarman.mockitokotlin2.times
import com.nhaarman.mockitokotlin2.verify
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
// Tests that pointer offsets are correct when a pointer is dispatched from Android through
// Compose and back into Android and each layer offsets the pointer during dispatch.
@MediumTest
@RunWith(JUnit4::class)
class PointerInteropFilterIntegrationTest {
private lateinit var five: View
private val theHitListener: () -> Unit = mock()
@get:Rule
val composeTestRule = AndroidComposeTestRule<TestActivity>()
@Before
fun setup() {
composeTestRule.activityRule.scenario.onActivity { activity ->
// one: Android View that is the touch target, inside
// two: Android View with 1x2 padding, inside
// three: Compose Box with 2x12 padding, inside
// four: Android View with 3x13 padding, inside
// five: Android View with 4x14 padding
//
// With all of the padding, "one" is at 10 x 50 relative to "five" and the tests
// dispatch MotionEvents to "five".
val one = CustomView(activity).apply {
layoutParams = ViewGroup.LayoutParams(1, 1)
hitListener = theHitListener
}
val two = FrameLayout(activity).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
setPadding(1, 11, 0, 0)
addView(one)
}
val four = FrameLayout(activity).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
setPadding(3, 13, 0, 0)
setContent(Recomposer.current()) {
with(DensityAmbient.current) {
// Box is "three"
Box(
paddingStart = (2f / density).dp,
paddingTop = (12f / density).dp
) {
AndroidView(two)
}
}
}
}
five = FrameLayout(activity).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
setPadding(4, 14, 0, 0)
addView(four)
}
activity.setContentView(five)
}
}
@Test
fun uiClick_inside_hits() {
uiClick(10, 50, true)
}
@Test
fun uiClick_justOutside_misses() {
uiClick(9, 50, false)
uiClick(10, 49, false)
uiClick(11, 50, false)
uiClick(10, 51, false)
}
// Gets reused to should always clean up state.
private fun uiClick(x: Int, y: Int, hits: Boolean) {
clearInvocations(theHitListener)
composeTestRule.activityRule.scenario.onActivity {
val down =
MotionEvent.obtain(
0L,
0L,
ACTION_DOWN,
x.toFloat(),
y.toFloat(),
0
)
val up =
MotionEvent.obtain(
0L,
1L,
ACTION_UP,
x.toFloat(),
y.toFloat(),
0
)
five.dispatchTouchEvent(down)
five.dispatchTouchEvent(up)
}
if (hits) {
verify(theHitListener, times(2)).invoke()
} else {
verify(theHitListener, never()).invoke()
}
}
}
private class CustomView(context: Context) : View(context) {
lateinit var hitListener: () -> Unit
override fun onTouchEvent(event: MotionEvent?): Boolean {
hitListener()
return true
}
}