From d7a865a29bbc6cfbf167427dad3bf0cb11b4fe49 Mon Sep 17 00:00:00 2001 From: yiqunw700 <165221276+yiqunw700@users.noreply.github.com> Date: Tue, 12 Nov 2024 00:37:45 -0800 Subject: [PATCH] Fix the tap callback for squoosh component instance (#1777) Fixes: #1709 --- .../designcompose/CustomizationContext.kt | 13 ++++ .../squoosh/SquooshInteraction.kt | 23 +++--- .../squoosh/SquooshTreeBuilder.kt | 3 +- ...tTapCallbackDoc_1jeKYynjk1nqYblZ66QDDK.dcf | Bin 0 -> 8760 bytes .../validation/examples/AllExamples.kt | 5 ++ .../examples/ComponentTapCallback.kt | 68 ++++++++++++++++++ .../testapp/validation/TapCallbackTest.kt | 54 ++++++++++++++ .../ComponentTapCallback.png | Bin 0 -> 16070 bytes .../ComponentTapCallback_SQUOOSH.png | Bin 0 -> 15858 bytes .../ComponentTapCallbackTest-tapped.png | Bin 0 -> 13419 bytes 10 files changed, 154 insertions(+), 12 deletions(-) create mode 100644 integration-tests/validation/src/main/assets/figma/ComponentTapCallbackDoc_1jeKYynjk1nqYblZ66QDDK.dcf create mode 100644 integration-tests/validation/src/main/java/com/android/designcompose/testapp/validation/examples/ComponentTapCallback.kt create mode 100644 integration-tests/validation/src/testDebug/kotlin/com/android/designcompose/testapp/validation/TapCallbackTest.kt create mode 100644 integration-tests/validation/src/testDebug/roborazzi/RenderAllExamples/ComponentTapCallback.png create mode 100644 integration-tests/validation/src/testDebug/roborazzi/RenderAllExamples/ComponentTapCallback_SQUOOSH.png create mode 100644 integration-tests/validation/src/testDebug/roborazzi/TapCallbackTest/ComponentTapCallbackTest-tapped.png diff --git a/designcompose/src/main/java/com/android/designcompose/CustomizationContext.kt b/designcompose/src/main/java/com/android/designcompose/CustomizationContext.kt index 8321cc500..691c31816 100644 --- a/designcompose/src/main/java/com/android/designcompose/CustomizationContext.kt +++ b/designcompose/src/main/java/com/android/designcompose/CustomizationContext.kt @@ -30,7 +30,9 @@ import com.android.designcompose.serdegen.ColorOrVarType import com.android.designcompose.serdegen.ComponentInfo import com.android.designcompose.serdegen.Dimension import com.android.designcompose.serdegen.NodeQuery +import com.android.designcompose.serdegen.View import java.util.Optional +import kotlin.jvm.optionals.getOrNull // Node data associated with a Figma node that may be a variant. This is used for grid layouts to // determine the span of an item in the grid layout. @@ -371,6 +373,17 @@ fun CustomizationContext.getTapCallback(nodeName: String): TapCallback? { return null } +fun CustomizationContext.getTapCallback(view: View): TapCallback? { + var tapCallback = getTapCallback(view.name) + // If no tap callback was found but this is a variant of a component set, + // look for a tap callback in the component set + val componentSetName = view.component_info.getOrNull()?.component_set_name + if (tapCallback == null && !componentSetName.isNullOrBlank()) { + tapCallback = getTapCallback(componentSetName) + } + return tapCallback +} + fun CustomizationContext.getContent(nodeName: String): ReplacementContent? { val c = cs[nodeName] ?: return null if (c.content.isPresent) return c.content.get() diff --git a/designcompose/src/main/java/com/android/designcompose/squoosh/SquooshInteraction.kt b/designcompose/src/main/java/com/android/designcompose/squoosh/SquooshInteraction.kt index fb2fef00b..a21678a10 100644 --- a/designcompose/src/main/java/com/android/designcompose/squoosh/SquooshInteraction.kt +++ b/designcompose/src/main/java/com/android/designcompose/squoosh/SquooshInteraction.kt @@ -73,11 +73,12 @@ internal fun Modifier.squooshInteraction( ): Modifier { val node = childComposable.node val maybeReactions = node.view.reactions - if (!maybeReactions.isPresent) return this - val reactions = maybeReactions.get() + val reactions = maybeReactions.getOrNull() + + val tapCallback = customizations.getTapCallback(node.view) return this.then( - Modifier.pointerInput(reactions) { + Modifier.pointerInput(reactions, tapCallback) { // Use the interaction scope so that we don't have our event handler removed when our // Modifier node is removed from the tree (allowing interactions like "close the overlay // while pressed" applied to an overlay to actually receive the touch release event and @@ -87,8 +88,8 @@ internal fun Modifier.squooshInteraction( onPress = { // Set the "pressed" state. reactions - .filter { r -> r.trigger.type is TriggerType.Press } - .forEach { + ?.filter { r -> r.trigger.type is TriggerType.Press } + ?.forEach { interactionState.dispatch( it.action.get(), findTargetInstanceId( @@ -104,8 +105,8 @@ internal fun Modifier.squooshInteraction( // Clear the "pressed" state. reactions - .filter { r -> r.trigger.type is TriggerType.Press } - .forEach { + ?.filter { r -> r.trigger.type is TriggerType.Press } + ?.forEach { interactionState.undoDispatch( findTargetInstanceId( document, @@ -121,8 +122,8 @@ internal fun Modifier.squooshInteraction( // of us, etc) then we can run the action. if (dispatchClickEvent) { reactions - .filter { r -> r.trigger.type is TriggerType.Click } - .forEach { + ?.filter { r -> r.trigger.type is TriggerType.Click } + ?.forEach { interactionState.dispatch( it.action.get(), findTargetInstanceId( @@ -135,8 +136,8 @@ internal fun Modifier.squooshInteraction( ) } } - // Invoke the tap callback customization if one exists on this node - customizations.getTapCallback(node.view.name)?.invoke() + + tapCallback?.invoke() } ) } diff --git a/designcompose/src/main/java/com/android/designcompose/squoosh/SquooshTreeBuilder.kt b/designcompose/src/main/java/com/android/designcompose/squoosh/SquooshTreeBuilder.kt index 6e7482841..9b7bfdbde 100644 --- a/designcompose/src/main/java/com/android/designcompose/squoosh/SquooshTreeBuilder.kt +++ b/designcompose/src/main/java/com/android/designcompose/squoosh/SquooshTreeBuilder.kt @@ -323,7 +323,8 @@ internal fun resolveVariantsRecursively( } } } - if (customizations.getTapCallback(view.name) != null) hasSupportedInteraction = true + val tapCallback = customizations.getTapCallback(view) + if (tapCallback != null) hasSupportedInteraction = true // If this node has a content customization, then we make a special record of it so that we can // zip through all of them after layout and render them in the right location. diff --git a/integration-tests/validation/src/main/assets/figma/ComponentTapCallbackDoc_1jeKYynjk1nqYblZ66QDDK.dcf b/integration-tests/validation/src/main/assets/figma/ComponentTapCallbackDoc_1jeKYynjk1nqYblZ66QDDK.dcf new file mode 100644 index 0000000000000000000000000000000000000000..7e3545a9dc5100a18cfd33e4bb565f29cdf07541 GIT binary patch literal 8760 zcmeHN&2AGh5Z>kgrywLyAD~=|luetYEkz~G9*PQ5sdC^)s3h$QLPJv0QVAiXiWA2^ z0#}5%N4k1 zxRRTw=7XuR(o`^c?*xfFAL`>wU%pjqv^-Bl<3TVL6dWO``absa(w_biw&wSxSSi&6 znI=1Pq1f502!%XT2PI^0F7&*;ifvn3SRXSQ+yyz?WuAxU?N*S_vaK0WQUj>QB#4Xr z`vJ8S)lK&kF4RiEHB>Z3Qrwy`^lXwX>8Th*Y|HF|9gYfvdW#L;3TYvCiq$IZr)$+{ zeZw~+K9-Z#TBo&gZ6ToyvFFI%nYH8aTDR56BE-=oKs2)N%S=rQJ5gdKdnP$#DSpNy zplvtM;tCM;4t8uTjRF&C@fw_W00VbsJ?}OAuuw88ClQ05w6e5WuofvmBT1NcVjA@% zKEfR&=KJ0FGDfc)U;xnD{w<_e%)^;U8znT;xr*n#Wrw+0hZ>R>sIL9dFDFDv4WJsk z&{ci|008t!3(nxY0~qL=Hsm5&SK2JlO;AW7l`xs8N(DPeNV&JzIO3gWr5pHCDdetV zH5LfcEYv!y(F&^|22hx%KgqVIcDF2TWmp>6kLcKfOho6_j35hkO{2*Q4K$?lX$Nv? z2!2%JQP95R63WOb6UfjdPLaK`2HM4Ty}i*EbMZTe9hM9WjwkAd6!eXj9&>Qs0Sr{Z z9o;gGmB1wO7cC=g7Cc2#z%|L7H6aYUMzM-15_a17L#*1Y)uZOpn#v{cRrlRzP~M(} z#|}O?j39aV9wi|%=kRsUfB}*VA5gOO5hWo!_$+~0A%m*7;-dRjJgem@v-7sLj(eKW zHN~spB;}~X+@Q#_@5xGbkJyR3nI?$Fg()Yn#or0BY6N9pJ$k=()zUymtxh!rUa^k% z4`mDaz{z{;B|jh!0pf=21Q=S{&S9tQ1js%yDX7^erg0wQJa;P0czq0$vk%=&^!mhn zqX6t%ISF8N2-~{u02q$p;>=MiV}J2>R@Rgqywn}tKl1T-EW00r#Le!{{}X}# z_T6qpt&M7Jtx{_?>$T-o1)!iPhd=wa9$WcIOQyddQ)K#vjAR#i0{LKkqLeS@$4iA$ zp_qfeiD4E6Ponv|FIrDlgVwXV_2#`|@pc%_%fx%tFGm_rYmcJ!k*SxjMn^WI<&Aa+ k8uuTJl3?j^ZT&F`mEuyp{k*lp>dVarxL`>v+<)-uClP!H!vFvP literal 0 HcmV?d00001 diff --git a/integration-tests/validation/src/main/java/com/android/designcompose/testapp/validation/examples/AllExamples.kt b/integration-tests/validation/src/main/java/com/android/designcompose/testapp/validation/examples/AllExamples.kt index a26177975..732b6f4bf 100644 --- a/integration-tests/validation/src/main/java/com/android/designcompose/testapp/validation/examples/AllExamples.kt +++ b/integration-tests/validation/src/main/java/com/android/designcompose/testapp/validation/examples/AllExamples.kt @@ -32,6 +32,11 @@ val EXAMPLES: ArrayList Unit, String?>> = Triple("Battleship", { BattleshipTest() }, BattleshipDoc.javaClass.name), Triple("Blend Modes", { BlendModeTest() }, BlendModeTestDoc.javaClass.name), Triple("Component Replace", { ComponentReplaceTest() }, ComponentReplaceDoc.javaClass.name), + Triple( + "ComponentTapCallback", + { ComponentTapCallbackTest() }, + ComponentTapCallbackDoc.javaClass.name, + ), Triple("Custom Brush", { CustomBrushTest() }, CustomBrushTestDoc.javaClass.name), // Dials gauges and progress vectors Triple("Dials Gauges", { DialsGaugesTest() }, DialsGaugesTestDoc.javaClass.name), diff --git a/integration-tests/validation/src/main/java/com/android/designcompose/testapp/validation/examples/ComponentTapCallback.kt b/integration-tests/validation/src/main/java/com/android/designcompose/testapp/validation/examples/ComponentTapCallback.kt new file mode 100644 index 000000000..c83da264e --- /dev/null +++ b/integration-tests/validation/src/main/java/com/android/designcompose/testapp/validation/examples/ComponentTapCallback.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2024 Google LLC + * + * 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.designcompose.testapp.validation.examples + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import com.android.designcompose.ComponentReplacementContext +import com.android.designcompose.TapCallback +import com.android.designcompose.annotation.Design +import com.android.designcompose.annotation.DesignComponent +import com.android.designcompose.annotation.DesignDoc +import com.android.designcompose.annotation.DesignVariant + +enum class DrivingState { + P, + R, + D, +} + +@DesignDoc(id = "1jeKYynjk1nqYblZ66QDDK") +interface ComponentTapCallback { + @DesignComponent(node = "#Main", isRoot = true) + fun mainFrame( + @Design(node = "CompTest") + drivingStateIndicator: @Composable (ComponentReplacementContext) -> Unit + ) + + @DesignComponent(node = "CompTest") + fun drivingStateIndicator( + @DesignVariant(property = "prnd") state: DrivingState, + @Design(node = "CompTest") onPress: TapCallback, + ) +} + +@Composable +fun ComponentTapCallbackTest() { + val drivingState = remember { mutableStateOf(DrivingState.P) } + ComponentTapCallbackDoc.mainFrame( + drivingStateIndicator = { + ComponentTapCallbackDoc.drivingStateIndicator( + state = drivingState.value, + onPress = { + drivingState.value = + when (drivingState.value) { + DrivingState.P -> DrivingState.R + DrivingState.R -> DrivingState.D + DrivingState.D -> DrivingState.P + } + }, + ) + } + ) +} diff --git a/integration-tests/validation/src/testDebug/kotlin/com/android/designcompose/testapp/validation/TapCallbackTest.kt b/integration-tests/validation/src/testDebug/kotlin/com/android/designcompose/testapp/validation/TapCallbackTest.kt new file mode 100644 index 000000000..d25c2af74 --- /dev/null +++ b/integration-tests/validation/src/testDebug/kotlin/com/android/designcompose/testapp/validation/TapCallbackTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2024 Google LLC + * + * 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.designcompose.testapp.validation + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.designcompose.TestUtils +import com.android.designcompose.test.internal.captureRootRoboImage +import com.android.designcompose.test.internal.designComposeRoborazziRule +import com.android.designcompose.testapp.common.InterFontTestRule +import com.android.designcompose.testapp.validation.examples.ComponentTapCallbackTest +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(AndroidJUnit4::class) +@Config(qualifiers = "w1920dp-h1500dp-xlarge-long-notround-any-xhdpi-keyshidden-nonav") +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class TapCallbackTest { + @get:Rule val clearStateTestRule = TestUtils.ClearStateTestRule() + @get:Rule val composeTestRule = createAndroidComposeRule() + @get:Rule val roborazziRule = designComposeRoborazziRule(javaClass.simpleName) + @get:Rule val interFontTestRule = InterFontTestRule() + + @Test + fun componentInstanceTapCallbackTest() { + with(composeTestRule) { + setContent { ComponentTapCallbackTest() } + onNodeWithText("Parked").assertExists() + onNodeWithText("Parked").performClick() + onNodeWithText("Reverse").assertExists() + captureRootRoboImage("ComponentTapCallbackTest-tapped") + } + } +} diff --git a/integration-tests/validation/src/testDebug/roborazzi/RenderAllExamples/ComponentTapCallback.png b/integration-tests/validation/src/testDebug/roborazzi/RenderAllExamples/ComponentTapCallback.png new file mode 100644 index 0000000000000000000000000000000000000000..8e5ac50f507a6ec67f7b756d78a349c047d1310a GIT binary patch literal 16070 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 literal 0 HcmV?d00001 diff --git a/integration-tests/validation/src/testDebug/roborazzi/RenderAllExamples/ComponentTapCallback_SQUOOSH.png b/integration-tests/validation/src/testDebug/roborazzi/RenderAllExamples/ComponentTapCallback_SQUOOSH.png new file mode 100644 index 0000000000000000000000000000000000000000..e2f6621ddbdaaf6febe816113546ad60252afbdf GIT binary patch literal 15858 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 tOK}J(x5_vub)d`xr4J~5K|b$|oTq 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