[go: nahoru, domu]

blob: 7ea1393b38d1683528ce8e6211fc5c051c4faae0 [file] [log] [blame]
Anastasia Sobolevaadfed532020-03-02 12:00:07 +00001/*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Louis Pullen-Freilicha11fd942020-07-24 19:31:19 +010017package androidx.compose.ui.layout
Anastasia Sobolevaadfed532020-03-02 12:00:07 +000018
Mihai Popa5836ac42020-11-06 10:36:03 +000019import androidx.compose.foundation.layout.Box
20import androidx.compose.foundation.layout.ExperimentalLayout
21import androidx.compose.foundation.layout.IntrinsicSize
22import androidx.compose.foundation.layout.preferredWidth
Louis Pullen-Freilich1f10a592020-07-24 16:35:14 +010023import androidx.compose.runtime.Composable
Anastasia Soboleva2833c362020-07-23 20:19:13 +010024import androidx.compose.runtime.Providers
Louis Pullen-Freilich534385a2020-08-14 14:10:12 +010025import androidx.compose.runtime.mutableStateOf
Louis Pullen-Freilicha11fd942020-07-24 19:31:19 +010026import androidx.compose.ui.FixedSize
Louis Pullen-Freilich534385a2020-08-14 14:10:12 +010027import androidx.compose.ui.Modifier
28import androidx.compose.ui.geometry.Offset
Louis Pullen-Freilicha03fd6c2020-07-24 23:26:29 +010029import androidx.compose.ui.node.Ref
Louis Pullen-Freilich209df682020-11-11 00:13:18 +000030import androidx.compose.ui.platform.AmbientLayoutDirection
Louis Pullen-Freilicha03fd6c2020-07-24 23:26:29 +010031import androidx.compose.ui.platform.setContent
Louis Pullen-Freilicha11fd942020-07-24 19:31:19 +010032import androidx.compose.ui.runOnUiThreadIR
33import androidx.compose.ui.test.TestActivity
Louis Pullen-Freilicha7eeb102020-07-22 17:54:24 +010034import androidx.compose.ui.unit.Density
Louis Pullen-Freilich534385a2020-08-14 14:10:12 +010035import androidx.compose.ui.unit.LayoutDirection
Filip Pavlisce1489432020-10-29 12:18:06 +000036import androidx.test.ext.junit.runners.AndroidJUnit4
Louis Pullen-Freilich534385a2020-08-14 14:10:12 +010037import androidx.test.filters.SmallTest
Mihai Popa5836ac42020-11-06 10:36:03 +000038import org.junit.Assert
Mihai Popafe2f8fa2020-04-21 18:00:21 +010039import org.junit.Assert.assertEquals
40import org.junit.Assert.assertTrue
Anastasia Sobolevaadfed532020-03-02 12:00:07 +000041import org.junit.Before
42import org.junit.Rule
43import org.junit.Test
44import org.junit.runner.RunWith
Anastasia Sobolevaadfed532020-03-02 12:00:07 +000045import java.util.concurrent.CountDownLatch
46import java.util.concurrent.TimeUnit
47
48@SmallTest
Jelle Fresen17628d72020-09-24 16:23:51 +010049@RunWith(AndroidJUnit4::class)
Anastasia Sobolevaadfed532020-03-02 12:00:07 +000050class RtlLayoutTest {
Aurimas Liutikas506ab64d2020-06-02 14:37:26 -070051 @Suppress("DEPRECATION")
Anastasia Sobolevaadfed532020-03-02 12:00:07 +000052 @get:Rule
Aurimas Liutikas506ab64d2020-06-02 14:37:26 -070053 val activityTestRule =
Louis Pullen-Freilicha11fd942020-07-24 19:31:19 +010054 androidx.test.rule.ActivityTestRule<TestActivity>(
Jeff Gastona3bbb7e2020-09-18 15:11:15 -040055 TestActivity::class.java
56 )
Louis Pullen-Freilicha11fd942020-07-24 19:31:19 +010057 private lateinit var activity: TestActivity
Anastasia Sobolevaadfed532020-03-02 12:00:07 +000058 internal lateinit var density: Density
59 internal lateinit var countDownLatch: CountDownLatch
Nader Jawad6df06122020-06-03 15:27:08 -070060 internal lateinit var position: Array<Ref<Offset>>
George Mount8f237572020-04-30 12:08:30 -070061 private val size = 100
Anastasia Sobolevaadfed532020-03-02 12:00:07 +000062
63 @Before
64 fun setup() {
65 activity = activityTestRule.activity
66 density = Density(activity)
67 activity.hasFocusLatch.await(5, TimeUnit.SECONDS)
Nader Jawad6df06122020-06-03 15:27:08 -070068 position = Array(3) { Ref<Offset>() }
Anastasia Sobolevaadfed532020-03-02 12:00:07 +000069 countDownLatch = CountDownLatch(3)
70 }
71
72 @Test
73 fun customLayout_absolutePositioning() = with(density) {
74 activityTestRule.runOnUiThreadIR {
Andrey Kulikov1526ea52020-04-07 18:43:27 +010075 activity.setContent {
Anastasia Sobolevaadfed532020-03-02 12:00:07 +000076 CustomLayout(true, LayoutDirection.Ltr)
77 }
78 }
79
80 countDownLatch.await(1, TimeUnit.SECONDS)
Louis Pullen-Freilicha11fd942020-07-24 19:31:19 +010081 assertEquals(Offset(0f, 0f), position[0].value)
82 assertEquals(Offset(size.toFloat(), size.toFloat()), position[1].value)
83 assertEquals(
Nader Jawad6df06122020-06-03 15:27:08 -070084 Offset(
George Mount8f237572020-04-30 12:08:30 -070085 (size * 2).toFloat(),
86 (size * 2).toFloat()
Nader Jawad6df06122020-06-03 15:27:08 -070087 ),
Anastasia Sobolevaadfed532020-03-02 12:00:07 +000088 position[2].value
89 )
90 }
91
92 @Test
93 fun customLayout_absolutePositioning_rtl() = with(density) {
94 activityTestRule.runOnUiThreadIR {
Andrey Kulikov1526ea52020-04-07 18:43:27 +010095 activity.setContent {
Anastasia Sobolevaadfed532020-03-02 12:00:07 +000096 CustomLayout(true, LayoutDirection.Rtl)
97 }
98 }
99
100 countDownLatch.await(1, TimeUnit.SECONDS)
Louis Pullen-Freilicha11fd942020-07-24 19:31:19 +0100101 assertEquals(
Nader Jawad6df06122020-06-03 15:27:08 -0700102 Offset(0f, 0f),
103 position[0].value
104 )
Louis Pullen-Freilicha11fd942020-07-24 19:31:19 +0100105 assertEquals(
Nader Jawad6df06122020-06-03 15:27:08 -0700106 Offset(
George Mount8f237572020-04-30 12:08:30 -0700107 size.toFloat(),
108 size.toFloat()
Nader Jawad6df06122020-06-03 15:27:08 -0700109 ),
110 position[1].value
111 )
Louis Pullen-Freilicha11fd942020-07-24 19:31:19 +0100112 assertEquals(
Nader Jawad6df06122020-06-03 15:27:08 -0700113 Offset(
George Mount8f237572020-04-30 12:08:30 -0700114 (size * 2).toFloat(),
115 (size * 2).toFloat()
Nader Jawad6df06122020-06-03 15:27:08 -0700116 ),
Anastasia Sobolevaadfed532020-03-02 12:00:07 +0000117 position[2].value
118 )
119 }
120
121 @Test
122 fun customLayout_positioning() = with(density) {
123 activityTestRule.runOnUiThreadIR {
Andrey Kulikov1526ea52020-04-07 18:43:27 +0100124 activity.setContent {
Anastasia Sobolevaadfed532020-03-02 12:00:07 +0000125 CustomLayout(false, LayoutDirection.Ltr)
126 }
127 }
128
129 countDownLatch.await(1, TimeUnit.SECONDS)
Louis Pullen-Freilicha11fd942020-07-24 19:31:19 +0100130 assertEquals(Offset(0f, 0f), position[0].value)
131 assertEquals(Offset(size.toFloat(), size.toFloat()), position[1].value)
132 assertEquals(
Nader Jawad6df06122020-06-03 15:27:08 -0700133 Offset(
George Mount8f237572020-04-30 12:08:30 -0700134 (size * 2).toFloat(),
135 (size * 2).toFloat()
Nader Jawad6df06122020-06-03 15:27:08 -0700136 ),
Anastasia Sobolevaadfed532020-03-02 12:00:07 +0000137 position[2].value
138 )
139 }
140
141 @Test
142 fun customLayout_positioning_rtl() = with(density) {
143 activityTestRule.runOnUiThreadIR {
Andrey Kulikov1526ea52020-04-07 18:43:27 +0100144 activity.setContent {
Anastasia Sobolevaadfed532020-03-02 12:00:07 +0000145 CustomLayout(false, LayoutDirection.Rtl)
146 }
147 }
148
149 countDownLatch.await(1, TimeUnit.SECONDS)
150
151 countDownLatch.await(1, TimeUnit.SECONDS)
Louis Pullen-Freilicha11fd942020-07-24 19:31:19 +0100152 assertEquals(
Nader Jawad6df06122020-06-03 15:27:08 -0700153 Offset(
George Mount8f237572020-04-30 12:08:30 -0700154 (size * 2).toFloat(),
Nader Jawad6df06122020-06-03 15:27:08 -0700155 0f
156 ),
157 position[0].value
158 )
Louis Pullen-Freilicha11fd942020-07-24 19:31:19 +0100159 assertEquals(
George Mount8f237572020-04-30 12:08:30 -0700160 Offset(size.toFloat(), size.toFloat()),
Nader Jawad6df06122020-06-03 15:27:08 -0700161 position[1].value
162 )
Louis Pullen-Freilicha11fd942020-07-24 19:31:19 +0100163 assertEquals(Offset(0f, (size * 2).toFloat()), position[2].value)
Anastasia Sobolevaadfed532020-03-02 12:00:07 +0000164 }
165
Mihai Popafc355b12020-04-15 17:34:17 +0100166 @Test
Andrey Kulikovf437d212020-05-06 14:25:10 +0100167 fun customLayout_updatingDirectionCausesRemeasure() {
168 val direction = mutableStateOf(LayoutDirection.Rtl)
169 var latch = CountDownLatch(1)
170 var actualDirection: LayoutDirection? = null
171
172 activityTestRule.runOnUiThread {
173 activity.setContent {
174 val children = @Composable {
Anastasia Soboleva5e382dd2020-06-17 21:51:38 +0100175 Layout({}) { _, _ ->
Andrey Kulikovf437d212020-05-06 14:25:10 +0100176 actualDirection = layoutDirection
177 latch.countDown()
George Mount8f237572020-04-30 12:08:30 -0700178 layout(100, 100) {}
Andrey Kulikovf437d212020-05-06 14:25:10 +0100179 }
180 }
Louis Pullen-Freilich209df682020-11-11 00:13:18 +0000181 Providers(AmbientLayoutDirection provides direction.value) {
Anastasia Soboleva2833c362020-07-23 20:19:13 +0100182 Layout(children) { measurables, constraints ->
183 layout(100, 100) {
Anastasia Soboleva63cab532020-08-06 11:06:37 +0100184 measurables.first().measure(constraints).placeRelative(0, 0)
Anastasia Soboleva2833c362020-07-23 20:19:13 +0100185 }
Andrey Kulikovf437d212020-05-06 14:25:10 +0100186 }
187 }
188 }
189 }
190 assertTrue(latch.await(1, TimeUnit.SECONDS))
191 assertEquals(LayoutDirection.Rtl, actualDirection)
192
193 latch = CountDownLatch(1)
194 activityTestRule.runOnUiThread { direction.value = LayoutDirection.Ltr }
195
196 assertTrue(latch.await(1, TimeUnit.SECONDS))
197 assertEquals(LayoutDirection.Ltr, actualDirection)
198 }
Mihai Popa5836ac42020-11-06 10:36:03 +0000199 @Test
200 fun testModifiedLayoutDirection_inMeasureScope() {
201 val latch = CountDownLatch(1)
202 val resultLayoutDirection = Ref<LayoutDirection>()
203
204 activityTestRule.runOnUiThread {
205 activity.setContent {
206 Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
207 Layout(content = {}) { _, _ ->
208 resultLayoutDirection.value = layoutDirection
209 latch.countDown()
210 layout(0, 0) {}
211 }
212 }
213 }
214 }
215
216 assertTrue(latch.await(1, TimeUnit.SECONDS))
217 assertTrue(LayoutDirection.Rtl == resultLayoutDirection.value)
218 }
219
220 @Test
221 fun testModifiedLayoutDirection_inIntrinsicsMeasure() {
222 val latch = CountDownLatch(1)
223 var resultLayoutDirection: LayoutDirection? = null
224
225 activityTestRule.runOnUiThread {
226 activity.setContent {
227 @OptIn(ExperimentalLayout::class)
228 Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
229 Layout(
230 content = {},
231 modifier = Modifier.preferredWidth(IntrinsicSize.Max),
232 minIntrinsicWidthMeasureBlock = { _, _ -> 0 },
233 minIntrinsicHeightMeasureBlock = { _, _ -> 0 },
234 maxIntrinsicWidthMeasureBlock = { _, _ ->
235 resultLayoutDirection = this.layoutDirection
236 latch.countDown()
237 0
238 },
239 maxIntrinsicHeightMeasureBlock = { _, _ -> 0 }
240 ) { _, _ ->
241 layout(0, 0) {}
242 }
243 }
244 }
245 }
246
247 assertTrue(latch.await(1, TimeUnit.SECONDS))
248 Assert.assertNotNull(resultLayoutDirection)
249 assertTrue(LayoutDirection.Rtl == resultLayoutDirection)
250 }
251
252 @Test
253 fun testRestoreLocaleLayoutDirection() {
254 val latch = CountDownLatch(1)
255 val resultLayoutDirection = Ref<LayoutDirection>()
256
257 activityTestRule.runOnUiThread {
258 activity.setContent {
259 val initialLayoutDirection = AmbientLayoutDirection.current
260 Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
261 Box {
262 Providers(AmbientLayoutDirection provides initialLayoutDirection) {
Leland Richardson18f74552021-01-25 18:34:20 -0800263 Layout({}) { _, _ ->
Mihai Popa5836ac42020-11-06 10:36:03 +0000264 resultLayoutDirection.value = layoutDirection
265 latch.countDown()
266 layout(0, 0) {}
267 }
268 }
269 }
270 }
271 }
272 }
273
274 assertTrue(latch.await(1, TimeUnit.SECONDS))
275 assertEquals(LayoutDirection.Ltr, resultLayoutDirection.value)
276 }
Andrey Kulikovf437d212020-05-06 14:25:10 +0100277
Anastasia Sobolevaadfed532020-03-02 12:00:07 +0000278 @Composable
279 private fun CustomLayout(
280 absolutePositioning: Boolean,
281 testLayoutDirection: LayoutDirection
282 ) {
Louis Pullen-Freilich209df682020-11-11 00:13:18 +0000283 Providers(AmbientLayoutDirection provides testLayoutDirection) {
Anastasia Soboleva2833c362020-07-23 20:19:13 +0100284 Layout(
Mihai Popa5836ac42020-11-06 10:36:03 +0000285 content = {
286 FixedSize(size, modifier = Modifier.saveLayoutInfo(position[0], countDownLatch))
287 FixedSize(size, modifier = Modifier.saveLayoutInfo(position[1], countDownLatch))
288 FixedSize(size, modifier = Modifier.saveLayoutInfo(position[2], countDownLatch))
Anastasia Soboleva2833c362020-07-23 20:19:13 +0100289 }
290 ) { measurables, constraints ->
291 val placeables = measurables.map { it.measure(constraints) }
292 val width = placeables.fold(0) { sum, p -> sum + p.width }
293 val height = placeables.fold(0) { sum, p -> sum + p.height }
294 layout(width, height) {
Andrey Kulikov33185fb2020-11-06 15:45:30 +0000295 var x = 0
296 var y = 0
Anastasia Soboleva2833c362020-07-23 20:19:13 +0100297 for (placeable in placeables) {
298 if (absolutePositioning) {
Andrey Kulikov33185fb2020-11-06 15:45:30 +0000299 placeable.place(x, y)
Anastasia Soboleva63cab532020-08-06 11:06:37 +0100300 } else {
Andrey Kulikov33185fb2020-11-06 15:45:30 +0000301 placeable.placeRelative(x, y)
Anastasia Soboleva2833c362020-07-23 20:19:13 +0100302 }
Andrey Kulikov33185fb2020-11-06 15:45:30 +0000303 x += placeable.width
304 y += placeable.height
Anastasia Soboleva2833c362020-07-23 20:19:13 +0100305 }
Anastasia Sobolevaadfed532020-03-02 12:00:07 +0000306 }
307 }
308 }
309 }
310
Louis Pullen-Freilich46bf1f12020-11-16 16:58:49 +0000311 private fun Modifier.saveLayoutInfo(
Nader Jawad6df06122020-06-03 15:27:08 -0700312 position: Ref<Offset>,
George Mountd02af602020-03-06 16:41:40 -0800313 countDownLatch: CountDownLatch
Louis Pullen-Freilich46bf1f12020-11-16 16:58:49 +0000314 ): Modifier = onGloballyPositioned {
Filip Pavlisa1dc2642020-07-15 19:23:33 +0100315 position.value = it.localToRoot(Offset(0f, 0f))
George Mountd02af602020-03-06 16:41:40 -0800316 countDownLatch.countDown()
Anastasia Sobolevaadfed532020-03-02 12:00:07 +0000317 }
Mihai Popafe2f8fa2020-04-21 18:00:21 +0100318}