Merge "Remove Google compile testing from Room compiler" into androidx-main
diff --git a/activity/activity-lint/src/main/java/androidx/activity/lint/ActivityResultFragmentVersionDetector.kt b/activity/activity-lint/src/main/java/androidx/activity/lint/ActivityResultFragmentVersionDetector.kt
index 989efba4..2b809b3 100644
--- a/activity/activity-lint/src/main/java/androidx/activity/lint/ActivityResultFragmentVersionDetector.kt
+++ b/activity/activity-lint/src/main/java/androidx/activity/lint/ActivityResultFragmentVersionDetector.kt
@@ -195,7 +195,8 @@
if (library.isNotEmpty()) {
val currentVersion = library.substringAfter("androidx.fragment:fragment:")
- if (library != currentVersion && FRAGMENT_VERSION.compareVersions(currentVersion)) {
+ .substringBeforeLast("-")
+ if (library != currentVersion && currentVersion < FRAGMENT_VERSION) {
locations.forEach { location ->
context.report(
ISSUE, expression, location,
@@ -221,12 +222,4 @@
}
return ""
}
-
- private fun String.compareVersions(other: String): Boolean {
- return when {
- length < other.length -> true
- length > other.length -> false
- else -> this > other
- }
- }
}
diff --git a/activity/activity-lint/src/test/java/androidx/activity/lint/ActivityResultFragmentVersionDetectorTest.kt b/activity/activity-lint/src/test/java/androidx/activity/lint/ActivityResultFragmentVersionDetectorTest.kt
index 5686ef3..9c2cc3b 100644
--- a/activity/activity-lint/src/test/java/androidx/activity/lint/ActivityResultFragmentVersionDetectorTest.kt
+++ b/activity/activity-lint/src/test/java/androidx/activity/lint/ActivityResultFragmentVersionDetectorTest.kt
@@ -82,6 +82,78 @@
}
@Test
+ fun expectPassNewerStableVersion() {
+ lint().files(
+ kotlin(
+ """
+ package com.example
+
+ import androidx.activity.result.ActivityResultCaller
+ import androidx.activity.result.contract.ActivityResultContract
+
+ val launcher = ActivityResultCaller().registerForActivityResult(ActivityResultContract())
+ """
+ ),
+ gradle(
+ "build.gradle",
+ """
+ dependencies {
+ api("androidx.fragment:fragment:1.4.0")
+ }
+ """
+ ).indented()
+ ).run().expectClean()
+ }
+
+ @Test
+ fun expectPassNewerSnapshotVersions() {
+ lint().files(
+ kotlin(
+ """
+ package com.example
+
+ import androidx.activity.result.ActivityResultCaller
+ import androidx.activity.result.contract.ActivityResultContract
+
+ val launcher = ActivityResultCaller().registerForActivityResult(ActivityResultContract())
+ """
+ ),
+ gradle(
+ "build.gradle",
+ """
+ dependencies {
+ api("androidx.fragment:fragment:1.4.0-SNAPSHOT")
+ }
+ """
+ ).indented()
+ ).run().expectClean()
+ }
+
+ @Test
+ fun expectPassNewerAlphaVersions() {
+ lint().files(
+ kotlin(
+ """
+ package com.example
+
+ import androidx.activity.result.ActivityResultCaller
+ import androidx.activity.result.contract.ActivityResultContract
+
+ val launcher = ActivityResultCaller().registerForActivityResult(ActivityResultContract())
+ """
+ ),
+ gradle(
+ "build.gradle",
+ """
+ dependencies {
+ api("androidx.fragment:fragment:1.4.0-alpha01")
+ }
+ """
+ ).indented()
+ ).run().expectClean()
+ }
+
+ @Test
fun expectPassRegisterForActivityResultProject() {
lint().files(
kotlin(
@@ -112,7 +184,7 @@
gradle(
"""
dependencies {
- api("androidx.fragment:fragment:1.3.0-alpha05")
+ api("androidx.fragment:fragment:1.2.4")
}
"""
),
@@ -144,7 +216,7 @@
gradle(
"""
dependencies {
- api("androidx.fragment:fragment:1.3.0-alpha05")
+ api("androidx.fragment:fragment:1.2.4")
}
"""
),
@@ -180,7 +252,7 @@
gradle(
"""
dependencies {
- api("androidx.fragment:fragment:1.3.0-alpha05")
+ api("androidx.fragment:fragment:1.2.4")
}
"""
),
@@ -232,14 +304,14 @@
"build.gradle",
"""
dependencies {
- implementation("androidx.fragment:fragment-ktx:1.3.0-alpha05")
+ implementation("androidx.fragment:fragment-ktx:1.2.4")
}
"""
).indented()
).withDependencyGraph(
"""
- +--- androidx.fragment:fragment-ktx:1.3.0-alpha05
- \--- androidx.fragment:fragment:1.3.0-alpha05
+ +--- androidx.fragment:fragment-ktx:1.2.4
+ \--- androidx.fragment:fragment:1.2.4
""".trimIndent()
)
diff --git a/appcompat/appcompat/src/main/res/values-ar/strings.xml b/appcompat/appcompat/src/main/res/values-ar/strings.xml
index 3613bd9..639b63b 100644
--- a/appcompat/appcompat/src/main/res/values-ar/strings.xml
+++ b/appcompat/appcompat/src/main/res/values-ar/strings.xml
@@ -31,7 +31,7 @@
<string name="abc_activity_chooser_view_see_all" msgid="1189761859438369441">"عرض الكل"</string>
<string name="abc_shareactionprovider_share_with_application" msgid="9055268688411532828">"مشاركة مع <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
<string name="abc_shareactionprovider_share_with" msgid="8875138169939072951">"مشاركة مع"</string>
- <string name="abc_capital_on" msgid="884982626291842264">"تفعيل"</string>
+ <string name="abc_capital_on" msgid="884982626291842264">"مفعّلة"</string>
<string name="abc_capital_off" msgid="4215997306490295099">"إيقاف"</string>
<string name="search_menu_title" msgid="6264217191555673260">"البحث"</string>
<string name="abc_prepend_shortcut_label" msgid="5520303668377388990">"القائمة+"</string>
diff --git a/appcompat/appcompat/src/main/res/values-nl/strings.xml b/appcompat/appcompat/src/main/res/values-nl/strings.xml
index 2a6959d..0e299d6 100644
--- a/appcompat/appcompat/src/main/res/values-nl/strings.xml
+++ b/appcompat/appcompat/src/main/res/values-nl/strings.xml
@@ -28,7 +28,7 @@
<string name="abc_searchview_description_submit" msgid="1486535517437947103">"Zoekopdracht verzenden"</string>
<string name="abc_searchview_description_voice" msgid="2293578557972875415">"Gesproken zoekopdracht"</string>
<string name="abc_activitychooserview_choose_application" msgid="2165779757652331008">"Een app selecteren"</string>
- <string name="abc_activity_chooser_view_see_all" msgid="1189761859438369441">"Alles weergeven"</string>
+ <string name="abc_activity_chooser_view_see_all" msgid="1189761859438369441">"Alles tonen"</string>
<string name="abc_shareactionprovider_share_with_application" msgid="9055268688411532828">"Delen met <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
<string name="abc_shareactionprovider_share_with" msgid="8875138169939072951">"Delen met"</string>
<string name="abc_capital_on" msgid="884982626291842264">"AAN"</string>
diff --git a/browser/browser/src/main/res/values-iw/strings.xml b/browser/browser/src/main/res/values-iw/strings.xml
index a0b8a11..f991a3ec 100644
--- a/browser/browser/src/main/res/values-iw/strings.xml
+++ b/browser/browser/src/main/res/values-iw/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="fallback_menu_item_open_in_browser" msgid="3413186855122069269">"פתיחה בדפדפן"</string>
- <string name="fallback_menu_item_copy_link" msgid="4566929209979330987">"להעתקת הקישור"</string>
+ <string name="fallback_menu_item_copy_link" msgid="4566929209979330987">"העתקת הקישור"</string>
<string name="fallback_menu_item_share_link" msgid="7145444925855055364">"לשיתוף הקישור"</string>
<string name="copy_toast_msg" msgid="3260749812566568062">"הקישור הועתק ללוח"</string>
</resources>
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index 937d8f7..623c266 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -35,11 +35,11 @@
val BIOMETRIC = Version("1.2.0-alpha03")
val BROWSER = Version("1.4.0-alpha01")
val BUILDSRC_TESTS = Version("1.0.0-alpha01")
- val CAMERA = Version("1.1.0-alpha03")
- val CAMERA_EXTENSIONS = Version("1.0.0-alpha23")
+ val CAMERA = Version("1.1.0-alpha04")
+ val CAMERA_EXTENSIONS = Version("1.0.0-alpha24")
val CAMERA_PIPE = Version("1.0.0-alpha01")
val CAMERA_VIDEO = Version("1.0.0-alpha01")
- val CAMERA_VIEW = Version("1.0.0-alpha23")
+ val CAMERA_VIEW = Version("1.0.0-alpha24")
val CARDVIEW = Version("1.1.0-alpha01")
val CAR_APP = Version("1.1.0-alpha01")
// Pre-release before confirming to the same version as the rest of the CAR_APP library group.
diff --git a/busytown/androidx_incremental.sh b/busytown/androidx_incremental.sh
new file mode 120000
index 0000000..c2e0027
--- /dev/null
+++ b/busytown/androidx_incremental.sh
@@ -0,0 +1 @@
+androidx.sh
\ No newline at end of file
diff --git a/car/app/app-samples/helloworld/automotive/src/main/AndroidManifest.xml b/car/app/app-samples/helloworld/automotive/src/main/AndroidManifest.xml
index 6fa3c62..f47864d 100644
--- a/car/app/app-samples/helloworld/automotive/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/helloworld/automotive/src/main/AndroidManifest.xml
@@ -19,7 +19,10 @@
package="androidx.car.app.sample.helloworld"
android:versionCode="1"
android:versionName="1.0">
- <application
+
+ <uses-feature android:name="android.software.car.templates_host" />
+
+ <application
android:label="@string/app_name"
android:icon="@drawable/ic_launcher">
<meta-data android:name="com.google.android.gms.car.application"
diff --git a/car/app/app-samples/navigation/automotive/src/main/AndroidManifest.xml b/car/app/app-samples/navigation/automotive/src/main/AndroidManifest.xml
index 14ec293..07bbd24 100644
--- a/car/app/app-samples/navigation/automotive/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/navigation/automotive/src/main/AndroidManifest.xml
@@ -28,7 +28,9 @@
<uses-permission android:name="androidx.car.app.ACCESS_SURFACE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
- <application
+ <uses-feature android:name="android.software.car.templates_host" />
+
+ <application
android:label="@string/app_name"
android:icon="@drawable/ic_launcher">
diff --git a/car/app/app-samples/places/automotive/src/main/AndroidManifest.xml b/car/app/app-samples/places/automotive/src/main/AndroidManifest.xml
index 48d79ae..9ea01b1 100644
--- a/car/app/app-samples/places/automotive/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/places/automotive/src/main/AndroidManifest.xml
@@ -27,6 +27,8 @@
<!-- For PlaceListMapTemplate -->
<uses-permission android:name="androidx.car.app.MAP_TEMPLATES"/>
+ <uses-feature android:name="android.software.car.templates_host" />
+
<application
android:label="@string/app_name"
android:icon="@drawable/ic_launcher">
diff --git a/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml b/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml
index fd89d70..5870edc 100644
--- a/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml
@@ -32,6 +32,8 @@
<uses-permission android:name="androidx.car.app.NAVIGATION_TEMPLATES"/>
<uses-permission android:name="androidx.car.app.ACCESS_SURFACE"/>
+ <uses-feature android:name="android.software.car.templates_host" />
+
<application
android:label="@string/app_name"
android:icon="@drawable/ic_launcher">
diff --git a/compose/ui/ui/api/1.0.0-beta05.txt b/compose/ui/ui/api/1.0.0-beta05.txt
index a926e4a..ce5c32e 100644
--- a/compose/ui/ui/api/1.0.0-beta05.txt
+++ b/compose/ui/ui/api/1.0.0-beta05.txt
@@ -2396,6 +2396,9 @@
method public operator <T> void set(androidx.compose.ui.semantics.SemanticsPropertyKey<T> key, T? value);
}
+ public final class SemanticsSortKt {
+ }
+
}
package androidx.compose.ui.state {
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index a926e4a..ce5c32e 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -2396,6 +2396,9 @@
method public operator <T> void set(androidx.compose.ui.semantics.SemanticsPropertyKey<T> key, T? value);
}
+ public final class SemanticsSortKt {
+ }
+
}
package androidx.compose.ui.state {
diff --git a/compose/ui/ui/api/public_plus_experimental_1.0.0-beta05.txt b/compose/ui/ui/api/public_plus_experimental_1.0.0-beta05.txt
index d101503..6383220 100644
--- a/compose/ui/ui/api/public_plus_experimental_1.0.0-beta05.txt
+++ b/compose/ui/ui/api/public_plus_experimental_1.0.0-beta05.txt
@@ -2535,6 +2535,9 @@
method public operator <T> void set(androidx.compose.ui.semantics.SemanticsPropertyKey<T> key, T? value);
}
+ public final class SemanticsSortKt {
+ }
+
}
package androidx.compose.ui.state {
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index d101503..6383220 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -2535,6 +2535,9 @@
method public operator <T> void set(androidx.compose.ui.semantics.SemanticsPropertyKey<T> key, T? value);
}
+ public final class SemanticsSortKt {
+ }
+
}
package androidx.compose.ui.state {
diff --git a/compose/ui/ui/api/restricted_1.0.0-beta05.txt b/compose/ui/ui/api/restricted_1.0.0-beta05.txt
index 8864bcd..313e7c8 100644
--- a/compose/ui/ui/api/restricted_1.0.0-beta05.txt
+++ b/compose/ui/ui/api/restricted_1.0.0-beta05.txt
@@ -2426,6 +2426,9 @@
method public operator <T> void set(androidx.compose.ui.semantics.SemanticsPropertyKey<T> key, T? value);
}
+ public final class SemanticsSortKt {
+ }
+
}
package androidx.compose.ui.state {
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index 8864bcd..313e7c8 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -2426,6 +2426,9 @@
method public operator <T> void set(androidx.compose.ui.semantics.SemanticsPropertyKey<T> key, T? value);
}
+ public final class SemanticsSortKt {
+ }
+
}
package androidx.compose.ui.state {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
index 3528238..b76d2e9 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
@@ -17,14 +17,25 @@
package androidx.compose.ui.semantics
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredSize
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.MeasurePolicy
+import androidx.compose.ui.layout.SubcomposeLayout
import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.ValueElement
import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
import androidx.compose.ui.platform.testTag
@@ -41,6 +52,11 @@
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastMap
import androidx.compose.ui.zIndex
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
@@ -702,7 +718,7 @@
val child2 = "child2"
rule.setContent {
SimpleTestLayout(
- Modifier.testTag(TestTag).semantics {}
+ Modifier.testTag(TestTag)
) {
SimpleTestLayout(
Modifier.zIndex(1f).semantics { contentDescription = child1 }
@@ -722,6 +738,437 @@
root.children[1].config.getOrNull(SemanticsProperties.ContentDescription)
)
}
+
+ @Test
+ fun testChildrenSortedByBounds_vertical_zIndex() {
+ val child1 = "child1"
+ val child2 = "child2"
+ rule.setContent {
+ Column(
+ Modifier.testTag(TestTag)
+ ) {
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(50.dp)
+ .zIndex(1f)
+ .semantics { contentDescription = child1 }
+ ) {}
+ SimpleTestLayout(
+ Modifier.requiredSize(50.dp).semantics { contentDescription = child2 }
+ ) {}
+ }
+ }
+
+ val root = rule.onNodeWithTag(TestTag).fetchSemanticsNode("can't find node $TestTag")
+ assertEquals(2, root.childrenSortedByBounds.size)
+ assertEquals(
+ child1,
+ root.childrenSortedByBounds[0].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ assertEquals(
+ child2,
+ root.childrenSortedByBounds[1].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ }
+
+ @Test
+ fun testChildrenSortedByBounds_horizontal_zIndex() {
+ val child1 = "child1"
+ val child2 = "child2"
+ rule.setContent {
+ Row(
+ Modifier.testTag(TestTag)
+ ) {
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(50.dp)
+ .zIndex(1f)
+ .semantics { contentDescription = child1 }
+ ) {}
+ SimpleTestLayout(
+ Modifier.requiredSize(50.dp).semantics { contentDescription = child2 }
+ ) {}
+ }
+ }
+
+ val root = rule.onNodeWithTag(TestTag).fetchSemanticsNode("can't find node $TestTag")
+ assertEquals(2, root.childrenSortedByBounds.size)
+ assertEquals(
+ child1,
+ root.childrenSortedByBounds[0].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ assertEquals(
+ child2,
+ root.childrenSortedByBounds[1].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ }
+
+ @Test
+ fun testChildrenSortedByBounds_vertical_offset() {
+ val child1 = "child1"
+ val child2 = "child2"
+ rule.setContent {
+ Box(
+ Modifier.testTag(TestTag)
+ ) {
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(50.dp)
+ .offset(x = 0.dp, y = 50.dp)
+ .semantics { contentDescription = child1 }
+ ) {}
+ SimpleTestLayout(
+ Modifier.requiredSize(50.dp).semantics { contentDescription = child2 }
+ ) {}
+ }
+ }
+
+ val root = rule.onNodeWithTag(TestTag).fetchSemanticsNode("can't find node $TestTag")
+ assertEquals(2, root.childrenSortedByBounds.size)
+ assertEquals(
+ child2,
+ root.childrenSortedByBounds[0].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ assertEquals(
+ child1,
+ root.childrenSortedByBounds[1].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ }
+
+ @Test
+ fun testChildrenSortedByBounds_horizontal_offset() {
+ val child1 = "child1"
+ val child2 = "child2"
+ rule.setContent {
+ Box(
+ Modifier.testTag(TestTag)
+ ) {
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(50.dp)
+ .offset(x = 50.dp, y = 0.dp)
+ .semantics { contentDescription = child1 }
+ ) {}
+ SimpleTestLayout(
+ Modifier.requiredSize(50.dp).semantics { contentDescription = child2 }
+ ) {}
+ }
+ }
+
+ val root = rule.onNodeWithTag(TestTag).fetchSemanticsNode("can't find node $TestTag")
+ assertEquals(2, root.childrenSortedByBounds.size)
+ assertEquals(
+ child2,
+ root.childrenSortedByBounds[0].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ assertEquals(
+ child1,
+ root.childrenSortedByBounds[1].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ }
+
+ @Test
+ fun testChildrenSortedByBounds_vertical_offset_overlapped() {
+ val child1 = "child1"
+ val child2 = "child2"
+ rule.setContent {
+ Box(
+ Modifier.testTag(TestTag)
+ ) {
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(50.dp)
+ .offset(x = 0.dp, y = 20.dp)
+ .semantics { contentDescription = child1 }
+ ) {}
+ SimpleTestLayout(
+ Modifier.requiredSize(50.dp).semantics { contentDescription = child2 }
+ ) {}
+ }
+ }
+
+ val root = rule.onNodeWithTag(TestTag).fetchSemanticsNode("can't find node $TestTag")
+ assertEquals(2, root.childrenSortedByBounds.size)
+ assertEquals(
+ child2,
+ root.childrenSortedByBounds[0].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ assertEquals(
+ child1,
+ root.childrenSortedByBounds[1].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ }
+
+ @Test
+ fun testChildrenSortedByBounds_horizontal_offset_overlapped() {
+ val child1 = "child1"
+ val child2 = "child2"
+ rule.setContent {
+ Box(
+ Modifier.testTag(TestTag)
+ ) {
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(50.dp)
+ .offset(x = 20.dp, y = 0.dp)
+ .semantics { contentDescription = child1 }
+ ) {}
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(50.dp)
+ .offset(x = 0.dp, y = 20.dp)
+ .semantics { contentDescription = child2 }
+ ) {}
+ }
+ }
+
+ val root = rule.onNodeWithTag(TestTag).fetchSemanticsNode("can't find node $TestTag")
+ assertEquals(2, root.childrenSortedByBounds.size)
+ assertEquals(
+ child2,
+ root.childrenSortedByBounds[0].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ assertEquals(
+ child1,
+ root.childrenSortedByBounds[1].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ }
+
+ @Test
+ fun testChildrenSortedByBounds_vertical_offset_overlapped_withPadding() {
+ val child1 = "child1"
+ val child2 = "child2"
+ rule.setContent {
+ Box(
+ Modifier.testTag(TestTag)
+ ) {
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(100.dp)
+ .offset(x = 25.dp, y = 20.dp)
+ .semantics { contentDescription = child1 }
+ ) {}
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(100.dp)
+ .padding(25.dp)
+ .semantics { contentDescription = child2 }
+ ) {}
+ }
+ }
+
+ val root = rule.onNodeWithTag(TestTag).fetchSemanticsNode("can't find node $TestTag")
+ assertEquals(2, root.childrenSortedByBounds.size)
+ assertEquals(
+ child1,
+ root.childrenSortedByBounds[0].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ assertEquals(
+ child2,
+ root.childrenSortedByBounds[1].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ }
+
+ @Test
+ fun testChildrenSortedByBounds_horizontal_offset_overlapped_withPadding() {
+ val child1 = "child1"
+ val child2 = "child2"
+ rule.setContent {
+ Box(
+ Modifier.testTag(TestTag)
+ ) {
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(100.dp)
+ .offset(x = 20.dp, y = 25.dp)
+ .semantics { contentDescription = child1 }
+ ) {}
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(100.dp)
+ .padding(25.dp)
+ .semantics { contentDescription = child2 }
+ ) {}
+ }
+ }
+
+ val root = rule.onNodeWithTag(TestTag).fetchSemanticsNode("can't find node $TestTag")
+ assertEquals(2, root.childrenSortedByBounds.size)
+ assertEquals(
+ child1,
+ root.childrenSortedByBounds[0].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ assertEquals(
+ child2,
+ root.childrenSortedByBounds[1].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ }
+
+ @Test
+ fun testChildrenSortedByBounds_vertical_subcompose() {
+ val child1 = "child1"
+ val child2 = "child2"
+ val density = Density(1f)
+ val size = with(density) { 100.dp.roundToPx() }.toFloat()
+ rule.setContent {
+ CompositionLocalProvider(LocalDensity provides density) {
+ SimpleSubcomposeLayout(
+ Modifier.testTag(TestTag),
+ {
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(100.dp)
+ .semantics { contentDescription = child1 }
+ ) {}
+ },
+ Offset(0f, size),
+ {
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(100.dp)
+ .semantics { contentDescription = child2 }
+ ) {}
+ },
+ Offset(0f, 0f)
+ )
+ }
+ }
+
+ val root = rule.onNodeWithTag(TestTag).fetchSemanticsNode("can't find node $TestTag")
+ assertEquals(2, root.childrenSortedByBounds.size)
+ assertEquals(
+ child2,
+ root.childrenSortedByBounds[0].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ assertEquals(
+ child1,
+ root.childrenSortedByBounds[1].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ }
+
+ @Test
+ fun testChildrenSortedByBounds_horizontal_subcompose() {
+ val child1 = "child1"
+ val child2 = "child2"
+ val density = Density(1f)
+ val size = with(density) { 100.dp.roundToPx() }.toFloat()
+ rule.setContent {
+ CompositionLocalProvider(LocalDensity provides density) {
+ SimpleSubcomposeLayout(
+ Modifier.testTag(TestTag),
+ {
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(100.dp)
+ .semantics { contentDescription = child1 }
+ ) {}
+ },
+ Offset(size, 0f),
+ {
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(100.dp)
+ .semantics { contentDescription = child2 }
+ ) {}
+ },
+ Offset(0f, 0f)
+ )
+ }
+ }
+
+ val root = rule.onNodeWithTag(TestTag).fetchSemanticsNode("can't find node $TestTag")
+ assertEquals(2, root.childrenSortedByBounds.size)
+ assertEquals(
+ child2,
+ root.childrenSortedByBounds[0].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ assertEquals(
+ child1,
+ root.childrenSortedByBounds[1].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ }
+
+ @Test
+ fun testChildrenSortedByBounds_rtl() {
+ val child1 = "child1"
+ val child2 = "child2"
+ val child3 = "child3"
+ val rtlChild1 = "rtlChild1"
+ val rtlChild2 = "rtlChild2"
+ val rtlChild3 = "rtlChild3"
+ rule.setContent {
+ Column(Modifier.testTag(TestTag)) {
+ Row {
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(100.dp)
+ .semantics { contentDescription = child1 }
+ ) {}
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(100.dp)
+ .semantics { contentDescription = child2 }
+ ) {}
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(100.dp)
+ .semantics { contentDescription = child3 }
+ ) {}
+ }
+ CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+ // Will display rtlChild3 rtlChild2 rtlChild1
+ Row {
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(100.dp)
+ .semantics { contentDescription = rtlChild1 }
+ ) {}
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(100.dp)
+ .semantics { contentDescription = rtlChild2 }
+ ) {}
+ SimpleTestLayout(
+ Modifier
+ .requiredSize(100.dp)
+ .semantics { contentDescription = rtlChild3 }
+ ) {}
+ }
+ }
+ }
+ }
+
+ val root = rule.onNodeWithTag(TestTag).fetchSemanticsNode("can't find node $TestTag")
+ assertEquals(6, root.childrenSortedByBounds.size)
+
+ // Ltr
+ assertEquals(
+ child1,
+ root.childrenSortedByBounds[0].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ assertEquals(
+ child2,
+ root.childrenSortedByBounds[1].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ assertEquals(
+ child3,
+ root.childrenSortedByBounds[2].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+
+ // Rtl
+ assertEquals(
+ rtlChild1,
+ root.childrenSortedByBounds[3].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ assertEquals(
+ rtlChild2,
+ root.childrenSortedByBounds[4].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ assertEquals(
+ rtlChild3,
+ root.childrenSortedByBounds[5].config.getOrNull(SemanticsProperties.ContentDescription)
+ )
+ }
}
private fun SemanticsNodeInteraction.assertDoesNotHaveProperty(property: SemanticsPropertyKey<*>) {
@@ -788,3 +1235,45 @@
}
}
}
+
+/**
+ * A simple SubComposeLayout which lays [contentOne] at [positionOne] and lays [contentTwo] at
+ * [positionTwo]. [contentOne] is placed first and [contentTwo] is placed second. Therefore, the
+ * semantics node for [contentOne] is before semantics node for [contentTwo] in
+ * [SemanticsNode.children].
+ */
+@Composable
+private fun SimpleSubcomposeLayout(
+ modifier: Modifier = Modifier,
+ contentOne: @Composable () -> Unit,
+ positionOne: Offset,
+ contentTwo: @Composable () -> Unit,
+ positionTwo: Offset
+) {
+ SubcomposeLayout(modifier) { constraints ->
+ val layoutWidth = constraints.maxWidth
+ val layoutHeight = constraints.maxHeight
+
+ val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
+
+ layout(layoutWidth, layoutHeight) {
+ val placeablesOne = subcompose(TestSlot.First, contentOne).fastMap {
+ it.measure(looseConstraints)
+ }
+
+ val placeablesTwo = subcompose(TestSlot.Second, contentTwo).fastMap {
+ it.measure(looseConstraints)
+ }
+
+ // Placing to control drawing order to match default elevation of each placeable
+ placeablesOne.fastForEach {
+ it.place(positionOne.x.toInt(), positionOne.y.toInt())
+ }
+ placeablesTwo.fastForEach {
+ it.place(positionTwo.x.toInt(), positionTwo.y.toInt())
+ }
+ }
+ }
+}
+
+private enum class TestSlot { First, Second }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
index febefb3..cb3b8ea 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
@@ -295,7 +295,7 @@
}
info.packageName = view.context.packageName
- semanticsNode.children.fastForEach { child ->
+ semanticsNode.childrenSortedByBounds.fastForEach { child ->
if (currentSemanticsNodes.contains(child.id)) {
val holder = view.androidViewsHandler.layoutNodeToHolder[child.layoutNode]
if (holder != null) {
@@ -1991,7 +1991,7 @@
fun concatenateChildrenContentDescriptionAndText(node: SemanticsNode): List<String> {
val childDescriptions = mutableListOf<String>()
- node.unmergedChildren().fastForEach { childNode ->
+ node.unmergedChildren(true).fastForEach { childNode ->
// Don't merge child that merges its children because that child node will be focused
// separately
if (childNode.unmergedConfig.isMergingSemanticsOfDescendants) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
index f131652..024cf1c 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
@@ -142,7 +142,7 @@
* In addition, if mergeDescendants and mergingEnabled are both true, then it
* also includes the semantics properties of descendant nodes.
*/
- // TODO(aelias): This is too expensive for a val (full subtree recreation every call);
+ // TODO(b/184376083): This is too expensive for a val (full subtree recreation every call);
// optimize this when the merging algorithm is improved.
val config: SemanticsConfiguration
get() {
@@ -171,10 +171,14 @@
private val isMergingSemanticsOfDescendants: Boolean
get() = mergingEnabled && unmergedConfig.isMergingSemanticsOfDescendants
- internal fun unmergedChildren(): List<SemanticsNode> {
+ internal fun unmergedChildren(sortByBounds: Boolean = false): List<SemanticsNode> {
val unmergedChildren: MutableList<SemanticsNode> = mutableListOf()
- val semanticsChildren = this.layoutNode.findOneLayerOfSemanticsWrappers()
+ val semanticsChildren = if (sortByBounds) {
+ this.layoutNode.findOneLayerOfSemanticsWrappersSortedByBounds()
+ } else {
+ this.layoutNode.findOneLayerOfSemanticsWrappers()
+ }
semanticsChildren.fastForEach { semanticsChild ->
unmergedChildren.add(SemanticsNode(semanticsChild, mergingEnabled))
}
@@ -182,30 +186,45 @@
return unmergedChildren
}
- /** Contains the children in inverse hit test order (i.e. paint order).
+ /**
+ * Contains the children in inverse hit test order (i.e. paint order).
*
* Note that if mergingEnabled and mergeDescendants are both true, then there
* are no children (except those that are themselves mergeDescendants).
*/
- // TODO(aelias): This is too expensive for a val (full subtree recreation every call);
+ // TODO(b/184376083): This is too expensive for a val (full subtree recreation every call);
// optimize this when the merging algorithm is improved.
val children: List<SemanticsNode>
- get() {
- // Replacing semantics never appear to have any children in the merged tree.
- if (mergingEnabled && unmergedConfig.isClearingSemantics) {
- return listOf()
- }
+ get() = getChildren(sortByBounds = false)
- if (isMergingSemanticsOfDescendants) {
- // In most common merging scenarios like Buttons, this will return nothing.
- // In cases like a clickable Row itself containing a Button, this will
- // return the Button as a child.
- return findOneLayerOfMergingSemanticsNodes()
- }
+ /**
+ * Contains the children sorted by bounds: top to down, left to right(right to left in RTL
+ * mode).
+ *
+ * Note that if mergingEnabled and mergeDescendants are both true, then there
+ * are no children (except those that are themselves mergeDescendants).
+ */
+ // TODO(b/184376083): This is too expensive for a val (full subtree recreation every call);
+ // optimize this when the merging algorithm is improved.
+ internal val childrenSortedByBounds: List<SemanticsNode>
+ get() = getChildren(sortByBounds = true)
- return unmergedChildren()
+ private fun getChildren(sortByBounds: Boolean): List<SemanticsNode> {
+ // Replacing semantics never appear to have any children in the merged tree.
+ if (mergingEnabled && unmergedConfig.isClearingSemantics) {
+ return listOf()
}
+ if (isMergingSemanticsOfDescendants) {
+ // In most common merging scenarios like Buttons, this will return nothing.
+ // In cases like a clickable Row itself containing a Button, this will
+ // return the Button as a child.
+ return findOneLayerOfMergingSemanticsNodes(sortByBounds = sortByBounds)
+ }
+
+ return unmergedChildren(sortByBounds)
+ }
+
/**
* Visits the immediate children of this node.
*
@@ -264,13 +283,14 @@
}
private fun findOneLayerOfMergingSemanticsNodes(
- list: MutableList<SemanticsNode> = mutableListOf<SemanticsNode>()
+ list: MutableList<SemanticsNode> = mutableListOf(),
+ sortByBounds: Boolean = false
): List<SemanticsNode> {
- unmergedChildren().fastForEach { child ->
- if (child.isMergingSemanticsOfDescendants == true) {
+ unmergedChildren(sortByBounds).fastForEach { child ->
+ if (child.isMergingSemanticsOfDescendants) {
list.add(child)
} else {
- if (child.unmergedConfig.isClearingSemantics == false) {
+ if (!child.unmergedConfig.isClearingSemantics) {
child.findOneLayerOfMergingSemanticsNodes(list)
}
}
@@ -328,7 +348,7 @@
}
private fun LayoutNode.findOneLayerOfSemanticsWrappers(
- list: MutableList<SemanticsWrapper> = mutableListOf<SemanticsWrapper>()
+ list: MutableList<SemanticsWrapper> = mutableListOf()
): List<SemanticsWrapper> {
zSortedChildren.forEach { child ->
val outerSemantics = child.outerSemantics
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsSort.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsSort.kt
new file mode 100644
index 0000000..10d921f
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsSort.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2021 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.compose.ui.semantics
+
+import androidx.compose.ui.layout.boundsInRoot
+import androidx.compose.ui.node.LayoutNode
+import androidx.compose.ui.node.LayoutNodeWrapper
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastMap
+
+// This part is a copy from ViewGroup#addChildrenForAccessibility.
+internal fun LayoutNode.findOneLayerOfSemanticsWrappersSortedByBounds(
+ list: MutableList<SemanticsWrapper> = mutableListOf()
+): List<SemanticsWrapper> {
+ fun sortWithStrategy(holders: List<NodeLocationHolder>): List<NodeLocationHolder> {
+ // This is gross but the least risky solution. The current comparison
+ // strategy breaks transitivity but produces very good results. Coming
+ // up with a new strategy requires time which we do not have, so ...
+ return try {
+ NodeLocationHolder.comparisonStrategy = NodeLocationHolder.ComparisonStrategy.Stripe
+ holders.toMutableList().apply { sort() }
+ } catch (iae: IllegalArgumentException) {
+ // Note that in practice this occurs extremely rarely in a couple
+ // of pathological cases.
+ NodeLocationHolder.comparisonStrategy = NodeLocationHolder.ComparisonStrategy.Location
+ holders.toMutableList().apply { sort() }
+ }
+ }
+
+ val holders = children.fastMap { NodeLocationHolder(this, it) }
+ val sortedChildren = sortWithStrategy(holders).fastMap { it.node }
+
+ sortedChildren.fastForEach { child ->
+ val outerSemantics = child.outerSemantics
+ if (outerSemantics != null) {
+ list.add(outerSemantics)
+ } else {
+ child.findOneLayerOfSemanticsWrappersSortedByBounds(list)
+ }
+ }
+ return list
+}
+
+internal class NodeLocationHolder internal constructor(
+ internal val subtreeRoot: LayoutNode,
+ internal val node: LayoutNode
+) : Comparable<NodeLocationHolder> {
+ internal companion object {
+ internal var comparisonStrategy = ComparisonStrategy.Stripe
+ }
+
+ internal enum class ComparisonStrategy { Stripe, Location }
+
+ private val location =
+ subtreeRoot.innerLayoutNodeWrapper.localBoundingBoxOf(node.findWrapperToGetBounds())
+ private val layoutDirection = subtreeRoot.layoutDirection
+
+ override fun compareTo(other: NodeLocationHolder): Int {
+ if (comparisonStrategy == ComparisonStrategy.Stripe) {
+ // First is above second.
+ if (location.bottom - other.location.top <= 0) {
+ return -1
+ }
+ // First is below second.
+ if (location.top - other.location.bottom >= 0) {
+ return 1
+ }
+ }
+
+ // We are ordering left-to-right, top-to-bottom.
+ if (layoutDirection == LayoutDirection.Ltr) {
+ val leftDifference = location.left - other.location.left
+ if (leftDifference != 0f) {
+ return if (leftDifference < 0) -1 else 1
+ }
+ } else { // RTL
+ val rightDifference = location.right - other.location.right
+ if (rightDifference != 0f) {
+ return if (rightDifference < 0) 1 else -1
+ }
+ }
+ // We are ordering left-to-right, top-to-bottom.
+ val topDifference = location.top - other.location.top
+ if (topDifference != 0f) {
+ return if (topDifference < 0) -1 else 1
+ }
+ // Break tie by height.
+ val heightDifference = location.height - other.location.height
+ if (heightDifference != 0f) {
+ return if (heightDifference < 0) 1 else -1
+ }
+ // Break tie by width.
+ val widthDifference = location.width - other.location.width
+ if (widthDifference != 0f) {
+ return if (widthDifference < 0) 1 else -1
+ }
+
+ // Find a child of each view with different screen bounds.
+ val view1Bounds = node.findWrapperToGetBounds().boundsInRoot()
+ val view2Bounds = other.node.findWrapperToGetBounds().boundsInRoot()
+ val child1 = node.findNodeByPredicateTraversal {
+ val tempRect = it.findWrapperToGetBounds().boundsInRoot()
+ view1Bounds != tempRect
+ }
+ val child2 = other.node.findNodeByPredicateTraversal {
+ val tempRect = it.findWrapperToGetBounds().boundsInRoot()
+ view2Bounds != tempRect
+ }
+ // Compare the children recursively
+ if ((child1 != null) && (child2 != null)) {
+ val childHolder1 = NodeLocationHolder(subtreeRoot, child1)
+ val childHolder2 = NodeLocationHolder(other.subtreeRoot, child2)
+ return childHolder1.compareTo(childHolder2)
+ }
+
+ // If only one has a child, use that one
+ if (child1 != null) {
+ return 1
+ }
+
+ if (child2 != null) {
+ return -1
+ }
+
+ // Break tie somehow
+ return -1
+ }
+}
+
+internal fun LayoutNode.findNodeByPredicateTraversal(
+ predicate: (LayoutNode) -> Boolean
+): LayoutNode? {
+ if (predicate(this)) {
+ return this
+ }
+
+ children.fastForEach {
+ val result = it.findNodeByPredicateTraversal(predicate)
+ if (result != null) {
+ return result
+ }
+ }
+
+ return null
+}
+
+/**
+ * If this node has semantics, we use the semantics wrapper to get bounds. Otherwise, we use
+ * innerLayoutNodeWrapper because it seems the bounds after padding is the effective content.
+ */
+internal fun LayoutNode.findWrapperToGetBounds(): LayoutNodeWrapper {
+ return outerMergingSemantics ?: outerSemantics ?: innerLayoutNodeWrapper
+}
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentAnimationTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentAnimationTest.kt
index 802a75e..a595660 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentAnimationTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentAnimationTest.kt
@@ -828,6 +828,41 @@
assertThat(fragment1.loadedAnimation).isEqualTo(EXIT)
}
+ @Test
+ fun removePopExitAnimationWithSetPrimaryNavigation() {
+ waitForAnimationReady()
+ val fm = activityRule.activity.supportFragmentManager
+ val fragment1 = AnimationFragment()
+ val fragment2 = AnimationFragment()
+
+ fm.beginTransaction()
+ .setReorderingAllowed(true)
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .add(R.id.fragmentContainer, fragment1, "fragment1")
+ .setPrimaryNavigationFragment(fragment1)
+ .addToBackStack("fragment1")
+ .commit()
+ activityRule.waitForExecution()
+
+ fm.beginTransaction()
+ .setReorderingAllowed(true)
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .replace(R.id.fragmentContainer, fragment2, "fragment2")
+ .setPrimaryNavigationFragment(fragment2)
+ .addToBackStack("fragment2")
+ .commit()
+ activityRule.waitForExecution()
+
+ assertThat(fragment1.loadedAnimation).isEqualTo(EXIT)
+ assertThat(fragment2.loadedAnimation).isEqualTo(ENTER)
+
+ fm.popBackStack()
+ activityRule.waitForExecution()
+
+ assertThat(fragment1.loadedAnimation).isEqualTo(POP_ENTER)
+ assertThat(fragment2.loadedAnimation).isEqualTo(POP_EXIT)
+ }
+
private fun assertEnterPopExit(fragment: AnimationFragment) {
assertFragmentAnimation(fragment, 1, true, ENTER)
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java b/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
index 68d5867..c67ee5a 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
@@ -416,29 +416,34 @@
if (f != null) {
f.mBeingSaved = mBeingSaved;
f.setPopDirection(false);
- f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
f.setNextTransition(mTransition);
f.setSharedElementNames(mSharedElementSourceNames, mSharedElementTargetNames);
}
switch (op.mCmd) {
case OP_ADD:
+ f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.setExitAnimationOrder(f, false);
mManager.addFragment(f);
break;
case OP_REMOVE:
+ f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.removeFragment(f);
break;
case OP_HIDE:
+ f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.hideFragment(f);
break;
case OP_SHOW:
+ f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.setExitAnimationOrder(f, false);
mManager.showFragment(f);
break;
case OP_DETACH:
+ f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.detachFragment(f);
break;
case OP_ATTACH:
+ f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.setExitAnimationOrder(f, false);
mManager.attachFragment(f);
break;
@@ -480,30 +485,35 @@
if (f != null) {
f.mBeingSaved = mBeingSaved;
f.setPopDirection(true);
- f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
f.setNextTransition(FragmentManager.reverseTransit(mTransition));
// Reverse the target and source names for pop operations
f.setSharedElementNames(mSharedElementTargetNames, mSharedElementSourceNames);
}
switch (op.mCmd) {
case OP_ADD:
+ f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.setExitAnimationOrder(f, true);
mManager.removeFragment(f);
break;
case OP_REMOVE:
+ f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.addFragment(f);
break;
case OP_HIDE:
+ f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.showFragment(f);
break;
case OP_SHOW:
+ f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.setExitAnimationOrder(f, true);
mManager.hideFragment(f);
break;
case OP_DETACH:
+ f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.attachFragment(f);
break;
case OP_ATTACH:
+ f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.setExitAnimationOrder(f, true);
mManager.detachFragment(f);
break;
diff --git a/media2/media2-widget/src/main/res/values-iw/strings.xml b/media2/media2-widget/src/main/res/values-iw/strings.xml
index 955c8fc..68731ca 100644
--- a/media2/media2-widget/src/main/res/values-iw/strings.xml
+++ b/media2/media2-widget/src/main/res/values-iw/strings.xml
@@ -26,7 +26,7 @@
<string name="MediaControlView_subtitle_track_number_text" msgid="2241078077382492349">"טראק <xliff:g id="TRACK_NUMBER">%1$d</xliff:g>"</string>
<string name="MediaControlView_subtitle_track_number_and_lang_text" msgid="2268860463481696609">"טראק <xliff:g id="TRACK_NUMBER">%1$d</xliff:g> – <xliff:g id="LANG">%2$s</xliff:g>"</string>
<string name="MediaControlView_audio_track_number_text" msgid="4263103361854223806">"טראק <xliff:g id="AUDIO_NUMBER">%1$d</xliff:g>"</string>
- <string name="mcv2_non_music_title_unknown_text" msgid="2032814146738922144">"כותרת הסרטון לא ידועה"</string>
+ <string name="mcv2_non_music_title_unknown_text" msgid="2032814146738922144">"שם הסרטון לא ידוע"</string>
<string name="mcv2_music_title_unknown_text" msgid="6037645626002038645">"שם השיר לא ידוע"</string>
<string name="mcv2_music_artist_unknown_text" msgid="5393558204040775454">"אומן לא ידוע"</string>
<string name="mcv2_playback_error_text" msgid="6061787693725630293">"לא ניתן להפעיל את הפריט שביקשת"</string>
@@ -37,7 +37,7 @@
<string name="mcv2_seek_bar_desc" msgid="24915699029009384">"התקדמות ההפעלה"</string>
<string name="mcv2_settings_button_desc" msgid="811917224044739656">"הגדרות"</string>
<string name="mcv2_cc_is_on" msgid="5427119422911561783">"הכתוביות מופעלות. כדי להסתירן, יש ללחוץ."</string>
- <string name="mcv2_cc_is_off" msgid="2380791179816122456">"הכתוביות כבויות. כדי להציגן, יש ללחוץ."</string>
+ <string name="mcv2_cc_is_off" msgid="2380791179816122456">"הכתוביות מושבתות. צריך ללחוץ כדי להציג אותן."</string>
<string name="mcv2_replay_button_desc" msgid="3128622733570179596">"הפעלה מחדש"</string>
<string name="mcv2_play_button_desc" msgid="4881308324856085359">"הפעלה"</string>
<string name="mcv2_pause_button_desc" msgid="7391720675120766278">"השהיה"</string>
diff --git a/preference/preference/res/values-ar/strings.xml b/preference/preference/res/values-ar/strings.xml
index 65864a0..c1f2e6c 100644
--- a/preference/preference/res/values-ar/strings.xml
+++ b/preference/preference/res/values-ar/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="v7_preference_on" msgid="89551595707643515">"تفعيل"</string>
+ <string name="v7_preference_on" msgid="89551595707643515">"مفعّلة"</string>
<string name="v7_preference_off" msgid="3140233346420563315">"إيقاف"</string>
<string name="expand_button_title" msgid="2427401033573778270">"إعدادات متقدمة"</string>
<string name="summary_collapsed_preference_list" msgid="9167775378838880170">"<xliff:g id="CURRENT_ITEMS">%1$s</xliff:g>، <xliff:g id="ADDED_ITEMS">%2$s</xliff:g>"</string>
diff --git a/preference/preference/res/values-fr-rCA/strings.xml b/preference/preference/res/values-fr-rCA/strings.xml
index e92d40a..56f7a88 100644
--- a/preference/preference/res/values-fr-rCA/strings.xml
+++ b/preference/preference/res/values-fr-rCA/strings.xml
@@ -3,7 +3,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="v7_preference_on" msgid="89551595707643515">"ACTIVÉ"</string>
<string name="v7_preference_off" msgid="3140233346420563315">"DÉSACTIVÉ"</string>
- <string name="expand_button_title" msgid="2427401033573778270">"Avancé"</string>
+ <string name="expand_button_title" msgid="2427401033573778270">"Paramètres avancés"</string>
<string name="summary_collapsed_preference_list" msgid="9167775378838880170">"<xliff:g id="CURRENT_ITEMS">%1$s</xliff:g>, <xliff:g id="ADDED_ITEMS">%2$s</xliff:g>"</string>
<string name="copy" msgid="6083905920877235314">"Copier"</string>
<string name="preference_copied" msgid="6685851473431805375">"« <xliff:g id="SUMMARY">%1$s</xliff:g> » copié dans le presse-papiers."</string>
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacElement.kt
index 7397551..47eb36b 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacElement.kt
@@ -35,21 +35,26 @@
annotation: KClass<T>,
containerAnnotation: KClass<out Annotation>?
): List<XAnnotationBox<T>> {
- return if (containerAnnotation == null) {
+ // if there is a container annotation and annotation is repeated, we'll get the container.
+ if (containerAnnotation != null) {
MoreElements
- .getAnnotationMirror(element, annotation.java)
- .orNull()
- ?.box(env, annotation.java)
- ?.let {
- listOf(it)
- }
- } else {
- val container = MoreElements
.getAnnotationMirror(element, containerAnnotation.java)
.orNull()
?.box(env, containerAnnotation.java)
- container?.getAsAnnotationBoxArray<T>("value")?.toList()
- } ?: emptyList()
+ ?.let { containerBox ->
+ // found a container, return
+ return containerBox.getAsAnnotationBoxArray<T>("value").toList()
+ }
+ }
+ // if there is no container annotation or annotation is not repeated, we'll see the
+ // individual value
+ return MoreElements
+ .getAnnotationMirror(element, annotation.java)
+ .orNull()
+ ?.box(env, annotation.java)
+ ?.let {
+ listOf(it)
+ } ?: emptyList()
}
override fun hasAnnotation(
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
index 06a3390..e0c3ef4 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
@@ -568,6 +568,54 @@
}
}
+ @Test
+ fun javaRepeatableAnnotation_notRepeated() {
+ val javaSrc = Source.java(
+ "JavaSubject",
+ """
+ import ${RepeatableJavaAnnotation::class.qualifiedName};
+ @RepeatableJavaAnnotation("x")
+ public class JavaSubject {}
+ """.trimIndent()
+ )
+ val kotlinSrc = Source.kotlin(
+ "KotlinSubject.kt",
+ """
+ import ${RepeatableJavaAnnotation::class.qualifiedName}
+ @RepeatableJavaAnnotation("x")
+ public class KotlinSubject
+ """.trimIndent()
+ )
+ runTest(
+ sources = listOf(javaSrc, kotlinSrc)
+ ) { invocation ->
+ listOf("JavaSubject", "KotlinSubject")
+ .map(invocation.processingEnv::requireTypeElement)
+ .forEach { subject ->
+ if (invocation.isKsp && preCompiled) {
+ // TODO remove once https://github.com/google/ksp/issues/356 is fixed
+ // KSP cannot read array of annotation values in compiled code
+ } else {
+ val annotations = subject.getAnnotations(
+ RepeatableJavaAnnotation::class
+ )
+ assertThat(
+ subject.hasAnnotation(
+ RepeatableJavaAnnotation::class
+ )
+ ).isTrue()
+ val values = annotations
+ .map {
+ it.value.value
+ }
+ assertWithMessage(subject.qualifiedName)
+ .that(values)
+ .containsExactly("x")
+ }
+ }
+ }
+ }
+
// helper function to read what we need
private fun XAnnotated.getSuppressValues(): Array<String>? {
return this.getAnnotation(TestSuppressWarnings::class)?.value?.value
diff --git a/slices/core/src/main/res/values-iw/strings.xml b/slices/core/src/main/res/values-iw/strings.xml
index 78b02aa..3064619 100644
--- a/slices/core/src/main/res/values-iw/strings.xml
+++ b/slices/core/src/main/res/values-iw/strings.xml
@@ -22,6 +22,6 @@
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- תהיה לה אפשרות לקרוא מידע מ-<xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- תהיה לה יכולת לנקוט פעולה בתוך <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_checkbox" msgid="5696872682700058611">"יש לאשר ל-<xliff:g id="APP">%1$s</xliff:g> להראות חלקים מכל אפליציה שהיא"</string>
- <string name="abc_slice_permission_allow" msgid="5024599872061409708">"יש אישור"</string>
+ <string name="abc_slice_permission_allow" msgid="5024599872061409708">"אישור"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"אני לא מרשה"</string>
</resources>
diff --git a/slices/core/src/main/res/values-nl/strings.xml b/slices/core/src/main/res/values-nl/strings.xml
index a847b8c..46b2c9f 100644
--- a/slices/core/src/main/res/values-nl/strings.xml
+++ b/slices/core/src/main/res/values-nl/strings.xml
@@ -17,11 +17,11 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="abc_slices_permission_request" msgid="3604847235923472451">"<xliff:g id="APP_0">%1$s</xliff:g> wil segmenten van <xliff:g id="APP_2">%2$s</xliff:g> weergeven"</string>
- <string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> toestaan om segmenten van <xliff:g id="APP_2">%2$s</xliff:g> weer te geven?"</string>
+ <string name="abc_slices_permission_request" msgid="3604847235923472451">"<xliff:g id="APP_0">%1$s</xliff:g> wil segmenten van <xliff:g id="APP_2">%2$s</xliff:g> tonen"</string>
+ <string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> toestaan om segmenten van <xliff:g id="APP_2">%2$s</xliff:g> te tonen?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Deze kan informatie lezen van <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Deze kan acties uitvoeren in <xliff:g id="APP">%1$s</xliff:g>"</string>
- <string name="abc_slice_permission_checkbox" msgid="5696872682700058611">"<xliff:g id="APP">%1$s</xliff:g> toestaan om segmenten van apps weer te geven"</string>
+ <string name="abc_slice_permission_checkbox" msgid="5696872682700058611">"<xliff:g id="APP">%1$s</xliff:g> toestaan om segmenten van apps te tonen"</string>
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Toestaan"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Weigeren"</string>
</resources>
diff --git a/slices/view/src/main/res/values-nl/strings.xml b/slices/view/src/main/res/values-nl/strings.xml
index b5d2edc..a5a10b3 100644
--- a/slices/view/src/main/res/values-nl/strings.xml
+++ b/slices/view/src/main/res/values-nl/strings.xml
@@ -19,7 +19,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="abc_slice_more" msgid="1983560225998630901">"Meer"</string>
- <string name="abc_slice_show_more" msgid="1567717014004692768">"Meer weergeven"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Meer tonen"</string>
<string name="abc_slice_updated" msgid="8155085405396453848">"Geüpdatet: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
<item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min geleden</item>
diff --git a/textclassifier/textclassifier/src/main/res/values-iw/strings.xml b/textclassifier/textclassifier/src/main/res/values-iw/strings.xml
index df4b5d1..a2bab86 100644
--- a/textclassifier/textclassifier/src/main/res/values-iw/strings.xml
+++ b/textclassifier/textclassifier/src/main/res/values-iw/strings.xml
@@ -21,7 +21,7 @@
<string name="dial" msgid="2101464860090311277">"ביצוע שיחה"</string>
<string name="dial_desc" msgid="1195407206406821463">"התקשרות למספר הטלפון שנבחר"</string>
<string name="browse" msgid="4753676368851695348">"פתיחה"</string>
- <string name="browse_desc" msgid="728882176434276995">"פתיחה של כתובת האתר שנבחרה"</string>
+ <string name="browse_desc" msgid="728882176434276995">"פתיחה של כתובת ה-URL שנבחרה"</string>
<string name="sms" msgid="2214738262605167054">"התכתבות בהודעות"</string>
<string name="sms_desc" msgid="7733202356868441148">"שליחת הודעה למספר הטלפון שנבחר"</string>
<string name="add_contact" msgid="386263007484061034">"הוספה"</string>