[go: nahoru, domu]

blob: 3ca3a92d8a53d3ca3069876f7cfba781438b2b05 [file] [log] [blame]
/*
* Copyright 2019 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.material
import androidx.animation.AnimationClockObservable
import androidx.compose.Composable
import androidx.compose.Model
import androidx.compose.Providers
import androidx.test.filters.MediumTest
import androidx.ui.core.LayoutCoordinates
import androidx.ui.core.TestTag
import androidx.ui.foundation.Box
import androidx.ui.foundation.Clickable
import androidx.ui.graphics.Canvas
import androidx.ui.graphics.Color
import androidx.ui.layout.LayoutPadding
import androidx.ui.layout.LayoutSize
import androidx.ui.layout.Row
import androidx.ui.layout.Stack
import androidx.ui.material.ripple.RippleThemeAmbient
import androidx.ui.material.ripple.Ripple
import androidx.ui.material.ripple.RippleEffect
import androidx.ui.material.ripple.RippleEffectFactory
import androidx.ui.material.ripple.RippleTheme
import androidx.ui.test.createComposeRule
import androidx.ui.test.doClick
import androidx.ui.test.findByTag
import androidx.ui.unit.Density
import androidx.ui.unit.Dp
import androidx.ui.unit.PxPosition
import androidx.ui.unit.dp
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
@MediumTest
@RunWith(JUnit4::class)
class RippleEffectTest {
@get:Rule
val composeTestRule = createComposeRule()
@Test
@Ignore
fun rippleEffectMatrixHasOffsetFromSurface() {
val latch = CountDownLatch(1)
val padding = 10.dp
composeTestRule.setMaterialContent {
RippleCallback(onRippleDrawn = {
latch.countDown()
}) {
Card {
Box(LayoutPadding(padding)) {
TestTag(tag = "ripple") {
RippleButton()
}
}
}
}
}
findByTag("ripple")
.doClick()
// wait for drawEffect to be called
assertTrue(latch.await(1, TimeUnit.SECONDS))
// TODO(Andrey): Move waitAndScreenShot() method for drawn pixel assertions from
// ui-foundation to ui-test to be able to use it here and reimplement this test
// by asserting the drawing is happening in the correct position by checking
// pixels on the result bitmap
// // verify matrix contains the expected padding
// assertNotNull(matrix)
// val paddingFloat = withDensity(composeTestRule.density) {
// padding.toIntPx().value.toFloat()
// }
// val expectedMatrix = Matrix4.translationValues(
// paddingFloat,
// paddingFloat,
// 0f
// )
// assertEquals(expectedMatrix, matrix)
}
@Test
fun rippleEffectMatrixHasTheClickedChildCoordinates() {
val latch = CountDownLatch(1)
val size = 10.dp
composeTestRule.setMaterialContent {
RippleCallback(onRippleDrawn = {
latch.countDown()
}) {
Card {
Stack {
Row {
RippleButton(size)
TestTag(tag = "ripple") {
RippleButton(size)
}
RippleButton(size)
}
}
}
}
}
findByTag("ripple")
.doClick()
// wait for drawEffect to be called
assertTrue(latch.await(1000, TimeUnit.SECONDS))
// TODO(Andrey): Move waitAndScreenShot() method for drawn pixel assertions from
// ui-foundation to ui-test to be able to use it here and reimplement this test
// by asserting the drawing is happening in the correct position by checking
// pixels on the result bitmap
// // verify matrix contains the expected padding
// assertNotNull(matrix)
// val offsetFloat = withDensity(composeTestRule.density) { size.toIntPx().value.toFloat() }
// val expectedMatrix = Matrix4.translationValues(
// offsetFloat,
// 0f,
// 0f
// )
// assertEquals(expectedMatrix, matrix)
}
@Test
fun twoEffectsDrawnAndDisposedCorrectly() {
val drawLatch = CountDownLatch(2)
val disposeLatch = CountDownLatch(2)
val emit = DoEmit(true)
composeTestRule.setMaterialContent {
RippleCallback(
onRippleDrawn = { drawLatch.countDown() },
onDispose = { disposeLatch.countDown() }
) {
Card {
if (emit.emit) {
Row {
TestTag(tag = "ripple") {
RippleButton(10.dp)
}
}
}
}
}
}
// create two effects
findByTag("ripple")
.doClick()
findByTag("ripple")
.doClick()
// wait for drawEffect to be called
assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
composeTestRule.runOnUiThread { emit.emit = false }
// wait for dispose to be called
assertTrue(disposeLatch.await(1, TimeUnit.SECONDS))
}
@Test
fun rippleColorAndOpacityAreTakenFromTheme() {
val drawLatch = CountDownLatch(1)
val color = Color.Yellow
val opacity = 0.2f
composeTestRule.setMaterialContent {
RippleCallback(
defaultColor = { color },
opacityCallback = { opacity },
onRippleDrawn = { actualColor ->
assertEquals(color.copy(alpha = opacity), actualColor)
drawLatch.countDown()
}
) {
TestTag(tag = "ripple") {
RippleButton(10.dp)
}
}
}
findByTag("ripple")
.doClick()
// wait for drawEffect to be called
assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
}
@Test
fun rippleOpacityIsTakenFromTheme() {
val drawLatch = CountDownLatch(1)
val color = Color.Green
val opacity = 0.2f
composeTestRule.setMaterialContent {
RippleCallback(
opacityCallback = { opacity },
onRippleDrawn = { actualColor ->
assertEquals(color.copy(alpha = opacity), actualColor)
drawLatch.countDown()
}
) {
TestTag(tag = "ripple") {
RippleButton(10.dp, color)
}
}
}
findByTag("ripple")
.doClick()
// wait for drawEffect to be called
assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
}
@Test
fun disabledRippleDoesntCreateEffects() {
val createdLatch = CountDownLatch(1)
composeTestRule.setMaterialContent {
RippleCallback(
onEffectCreated = { createdLatch.countDown() }
) {
Card {
TestTag(tag = "ripple") {
RippleButton(enabled = false)
}
}
}
}
// create two effects
findByTag("ripple")
.doClick()
// assert no effects has been created
assertFalse(createdLatch.await(500, TimeUnit.MILLISECONDS))
}
@Composable
private fun RippleButton(size: Dp? = null, color: Color? = null, enabled: Boolean = true) {
Ripple(bounded = false, color = color, enabled = enabled) {
Clickable(onClick = {}) {
Box(LayoutSize.Min(size ?: 0.dp))
}
}
}
@Composable
private fun RippleCallback(
onRippleDrawn: (Color) -> Unit = {},
onDispose: () -> Unit = {},
onEffectCreated: () -> Unit = {},
defaultColor: @Composable() () -> Color = { Color(0) },
opacityCallback: @Composable() () -> Float = { 1f },
children: @Composable() () -> Unit
) {
val theme = RippleTheme(
testRippleEffect(onRippleDrawn, onDispose, onEffectCreated),
defaultColor,
opacityCallback
)
Providers(RippleThemeAmbient provides theme, children = children)
}
private fun testRippleEffect(
onDraw: (Color) -> Unit,
onDispose: () -> Unit,
onEffectCreated: () -> Unit
): RippleEffectFactory =
object : RippleEffectFactory {
override fun create(
coordinates: LayoutCoordinates,
startPosition: PxPosition,
density: Density,
radius: Dp?,
clipped: Boolean,
clock: AnimationClockObservable,
requestRedraw: () -> Unit,
onAnimationFinished: (RippleEffect) -> Unit
): RippleEffect {
onEffectCreated()
return object : RippleEffect {
private var onDrawCalled: Boolean = false
override fun draw(canvas: Canvas, color: Color) {
if (!onDrawCalled) {
onDraw(color)
onDrawCalled = true
}
}
override fun finish(canceled: Boolean) {
}
override fun dispose() {
onDispose()
}
}
}
}
}
@Model
private data class DoEmit(var emit: Boolean)