diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 912060fc9..08333e6fa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -81,7 +81,7 @@ jobs: # Allow tests to continue on other devices if they fail on one device. fail-fast: false matrix: - api-level: [ 22, 26, 29 ] + api-level: [ 22, 26, 30 ] shard: [ 0, 1 ] # Need to update shard-count below if this changes env: @@ -96,6 +96,12 @@ jobs: - name: Copy CI gradle.properties run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties + - name: Enable KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + - name: Setup java uses: actions/setup-java@v3 with: @@ -126,7 +132,7 @@ jobs: API_LEVEL: ${{ matrix.api-level }} run: | ARCH="x86" - if [ "$API_LEVEL" -ge "31" ]; then + if [ "$API_LEVEL" -ge "29" ]; then ARCH="x86_64" fi echo "ARCH=$ARCH" >> $GITHUB_OUTPUT diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 000000000..a670cf458 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,18 @@ + + + + + + + + + \ No newline at end of file diff --git a/flowlayout/src/sharedTest/kotlin/com/google/accompanist/flowlayout/LayoutTest.kt b/flowlayout/src/sharedTest/kotlin/com/google/accompanist/flowlayout/LayoutTest.kt index 6827e0ec8..45d979b13 100644 --- a/flowlayout/src/sharedTest/kotlin/com/google/accompanist/flowlayout/LayoutTest.kt +++ b/flowlayout/src/sharedTest/kotlin/com/google/accompanist/flowlayout/LayoutTest.kt @@ -60,7 +60,7 @@ open class LayoutTest { size: Ref, position: Ref, positionedLatch: CountDownLatch - ): Modifier = this then onGloballyPositioned { coordinates -> + ): Modifier = onGloballyPositioned { coordinates -> size.value = IntSize(coordinates.size.width, coordinates.size.height) position.value = coordinates.localToRoot(Offset(0f, 0f)) positionedLatch.countDown() diff --git a/gradle.properties b/gradle.properties index c66568c7b..f827bf0c9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -33,7 +33,7 @@ systemProp.org.gradle.internal.http.socketTimeout=120000 GROUP=com.google.accompanist # !! No longer need to update this manually when using a Compose SNAPSHOT -VERSION_NAME=0.35.2-SNAPSHOT +VERSION_NAME=0.35.2-beta POM_DESCRIPTION=Utilities for Jetpack Compose diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 307fcd00f..2fcdafbc5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] -compose = "1.7.0-alpha02" +compose = "1.7.0-beta07" composeCompiler = "1.5.8" composeMaterial3 = "1.0.1" composesnapshot = "-" # a single character = no snapshot @@ -8,7 +8,7 @@ composesnapshot = "-" # a single character = no snapshot dokka = "1.8.10" # gradlePlugin and lint need to be updated together -gradlePlugin = "8.2.2" +gradlePlugin = "8.5.2" lintMinCompose = "30.0.0" ktlint = "0.45.2" @@ -18,8 +18,8 @@ okhttp = "3.12.13" coil = "1.3.2" androidlint = "25.3.0" -androidxtest = "1.4.0" -androidxnavigation = "2.7.0-alpha01" +androidxtest = "1.6.1" +androidxnavigation = "2.7.7" androidxWindow = "1.0.0" metalava = "0.3.2" @@ -68,8 +68,8 @@ coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } androidx-appcompat = "androidx.appcompat:appcompat:1.4.2" androidx-core = "androidx.core:core-ktx:1.8.0" -androidx-activity-compose = "androidx.activity:activity-compose:1.7.2" -androidx-fragment = "androidx.fragment:fragment-ktx:1.5.1" +androidx-activity-compose = "androidx.activity:activity-compose:1.9.0" +androidx-fragment = "androidx.fragment:fragment-ktx:1.8.1" androidx-dynamicanimation = "androidx.dynamicanimation:dynamicanimation-ktx:1.0.0-alpha03" androidx-lifecycle-runtime = "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1" androidx-lifecycle-viewmodel-compose = "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1" @@ -82,19 +82,18 @@ androidx-navigation-testing = { module = "androidx.navigation:navigation-testing mdc = "com.google.android.material:material:1.8.0" -androidx-test-core = "androidx.test:core-ktx:1.5.0-alpha02" -androidx-test-runner = "androidx.test:runner:1.5.0-alpha04" +androidx-test-core = "androidx.test:core-ktx:1.6.1" +androidx-test-runner = "androidx.test:runner:1.6.1" androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidxtest" } -androidx-test-orchestrator = "androidx.test:orchestrator:1.4.1" -androidx-test-uiAutomator = "androidx.test.uiautomator:uiautomator:2.2.0" +androidx-test-orchestrator = "androidx.test:orchestrator:1.5.0" +androidx-test-uiAutomator = "androidx.test.uiautomator:uiautomator:2.3.0" -# alpha for robolectric x compose fix -androidx-test-espressoCore = "androidx.test.espresso:espresso-core:3.5.1" -androidx-test-espressoWeb = "androidx.test.espresso:espresso-web:3.5.1" +androidx-test-espressoCore = "androidx.test.espresso:espresso-core:3.6.1" +androidx-test-espressoWeb = "androidx.test.espresso:espresso-web:3.6.1" junit = "junit:junit:4.13.2" -truth = "com.google.truth:truth:1.1.2" -robolectric = "org.robolectric:robolectric:4.9" +truth = "com.google.truth:truth:1.1.3" +robolectric = "org.robolectric:robolectric:4.12.1" affectedmoduledetector = "com.dropbox.affectedmoduledetector:affectedmoduledetector:0.1.2" @@ -103,7 +102,7 @@ android-tools-lint-lint = { module = "com.android.tools.lint:lint", version.ref android-tools-lint-api = { module = "com.android.tools.lint:lint-api", version.ref = "lintMinCompose" } android-tools-lint-tests = { module = "com.android.tools.lint:lint-tests", version.ref = "lintMinCompose" } -squareup-mockwebserver = "com.squareup.okhttp3:mockwebserver:4.9.3" +squareup-mockwebserver = "com.squareup.okhttp3:mockwebserver:4.10.0" [plugins] android-application = { id = "com.android.application", version.ref = "gradlePlugin" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3499ded5c..d62b91ae6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ +#Wed Jul 10 11:49:25 AEST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/navigation-material/src/androidTest/java/com/google/accompanist/navigation.material/SheetContentHostTest.kt b/navigation-material/src/androidTest/java/com/google/accompanist/navigation.material/SheetContentHostTest.kt index bf41d7a2d..1f199579e 100644 --- a/navigation-material/src/androidTest/java/com/google/accompanist/navigation.material/SheetContentHostTest.kt +++ b/navigation-material/src/androidTest/java/com/google/accompanist/navigation.material/SheetContentHostTest.kt @@ -28,9 +28,13 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.State +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveableStateHolder +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.onPlaced import androidx.compose.ui.platform.testTag import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.junit4.createComposeRule @@ -174,10 +178,8 @@ internal class SheetContentHostTest { onSheetDismissed: (NavBackStackEntry) -> Unit ) { setContent { + var anchored by remember { mutableStateOf(false) } val saveableStateHolder = rememberSaveableStateHolder() - LaunchedEffect(backStackEntry.value) { - if (backStackEntry.value == null) sheetState.hide() else sheetState.show() - } ModalBottomSheetLayout( sheetContent = { SheetContentHost( @@ -189,6 +191,7 @@ internal class SheetContentHostTest { ) }, sheetState = sheetState, + modifier = Modifier.onPlaced { anchored = true }, content = { Box( Modifier @@ -197,6 +200,12 @@ internal class SheetContentHostTest { ) } ) + + if (anchored) { + LaunchedEffect(backStackEntry.value) { + if (backStackEntry.value == null) sheetState.hide() else sheetState.show() + } + } } } diff --git a/permissions/src/androidTest/AndroidManifest.xml b/permissions/src/androidTest/AndroidManifest.xml index 7a37a0c0e..8bf897268 100644 --- a/permissions/src/androidTest/AndroidManifest.xml +++ b/permissions/src/androidTest/AndroidManifest.xml @@ -17,7 +17,7 @@ - + simulateAppComingFromTheBackground( composeTestRule: AndroidComposeTestRule, T> @@ -57,22 +59,15 @@ internal fun grantPermissionInDialog( ) { val uiDevice = UiDevice.getInstance(instrumentation) val sdkVersion = Build.VERSION.SDK_INT - val clicked = uiDevice.findPermissionButton( + val button = uiDevice.findPermissionButton( when (sdkVersion) { in 24..28 -> "ALLOW" - else -> "Allow" + 29 -> "Allow" + else -> "While using the app" } - ).clickForPermission(instrumentation) - - // Or maybe this permission doesn't have the Allow option - if (!clicked && sdkVersion > 28) { - uiDevice.findPermissionButton( - when (sdkVersion) { - 29 -> "Allow only while using the app" - else -> "While using the app" - } - ).clickForPermission(instrumentation) - } + ) + + button.clickForPermission(instrumentation) } internal fun denyPermissionInDialog( @@ -81,7 +76,7 @@ internal fun denyPermissionInDialog( val text = when (Build.VERSION.SDK_INT) { in 24..28 -> "DENY" in 29..30 -> "Deny" - else -> "Don’t allow" + else -> "t allow" // Different sdks and devices seem to have either ' or ’ } val permissionButton = UiDevice.getInstance(instrumentation).findPermissionButton(text) permissionButton.clickForPermission(instrumentation) @@ -95,54 +90,53 @@ internal fun doNotAskAgainPermissionInDialog( Build.VERSION.SDK_INT >= 30 -> { denyPermissionInDialog(instrumentation) } + Build.VERSION.SDK_INT > 28 -> { uiDevice .findPermissionButton("Deny & don’t ask again") .clickForPermission(instrumentation) } + Build.VERSION.SDK_INT == 23 -> { - uiDevice.findObject( - UiSelector().text("Never ask again") - ).clickForPermission(instrumentation) + uiDevice.findPermissionButton("Never ask again") + .clickForPermission(instrumentation) denyPermissionInDialog(instrumentation) } + else -> { - uiDevice.findObject( - UiSelector().text("Don't ask again") + uiDevice.findPermissionButton( + "Don't ask again" ).clickForPermission(instrumentation) denyPermissionInDialog(instrumentation) } } } -private fun UiDevice.findPermissionButton(text: String): UiObject = - findObject( - UiSelector() - .textMatches(text) - .clickable(true) - .className("android.widget.Button") - ) +private fun UiDevice.findPermissionButton( + text: String +): UiObject2 { + val selector = By + .textContains(text) + .clickable(true) -private fun UiObject.clickForPermission( - instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() -): Boolean { - waitUntil { exists() } - if (!exists()) return false + val found = wait(Until.hasObject(selector), 3000) - val clicked = waitUntil { exists() && click() } - // Make sure that the tests waits for this click to be processed - if (clicked) { instrumentation.waitForIdleSync() } - return clicked -} + if (!found) { + val output = ByteArrayOutputStream() + dumpWindowHierarchy(output) + println(output.toByteArray().decodeToString()) -private fun waitUntil(timeoutMillis: Long = 2_000, condition: () -> Boolean): Boolean { - val startTime = System.nanoTime() - while (true) { - if (condition()) return true - // Let Android run measure, draw and in general any other async operations. - Thread.sleep(10) - if (System.nanoTime() - startTime > timeoutMillis * 1_000_000) { - return false - } + error("Could not find button with text $text") } + + return findObject(selector) +} + +private fun UiObject2.clickForPermission( + instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() +): Boolean { + click() + // Make sure that the tests waits for this click to be processed + instrumentation.waitForIdleSync() + return true } diff --git a/permissions/src/main/java/com/google/accompanist/permissions/PermissionsUtil.kt b/permissions/src/main/java/com/google/accompanist/permissions/PermissionsUtil.kt index a15a01e21..9ba75827b 100644 --- a/permissions/src/main/java/com/google/accompanist/permissions/PermissionsUtil.kt +++ b/permissions/src/main/java/com/google/accompanist/permissions/PermissionsUtil.kt @@ -24,7 +24,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.Stable import androidx.compose.runtime.remember -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.lifecycle.Lifecycle @@ -87,7 +86,7 @@ internal fun PermissionLifecycleCheckerEffect( } } } - val lifecycle = LocalLifecycleOwner.current.lifecycle + val lifecycle = androidx.lifecycle.compose.LocalLifecycleOwner.current.lifecycle DisposableEffect(lifecycle, permissionCheckerObserver) { lifecycle.addObserver(permissionCheckerObserver) onDispose { lifecycle.removeObserver(permissionCheckerObserver) } @@ -119,7 +118,7 @@ internal fun PermissionsLifecycleCheckerEffect( } } } - val lifecycle = LocalLifecycleOwner.current.lifecycle + val lifecycle = androidx.lifecycle.compose.LocalLifecycleOwner.current.lifecycle DisposableEffect(lifecycle, permissionsCheckerObserver) { lifecycle.addObserver(permissionsCheckerObserver) onDispose { lifecycle.removeObserver(permissionsCheckerObserver) } diff --git a/sample/src/main/java/com/google/accompanist/sample/adaptive/DraggableFoldAwareColumnSample.kt b/sample/src/main/java/com/google/accompanist/sample/adaptive/DraggableFoldAwareColumnSample.kt index d4d190db2..925753e25 100644 --- a/sample/src/main/java/com/google/accompanist/sample/adaptive/DraggableFoldAwareColumnSample.kt +++ b/sample/src/main/java/com/google/accompanist/sample/adaptive/DraggableFoldAwareColumnSample.kt @@ -67,7 +67,6 @@ class DraggableFoldAwareColumnSample : ComponentActivity() { @Composable fun DraggableExample(activity: Activity) { var offset by remember { mutableStateOf(Offset(0f, 0f)) } - FoldAwareColumn( modifier = Modifier .offset { IntOffset(offset.x.roundToInt(), offset.y.roundToInt()) } diff --git a/sample/src/main/java/com/google/accompanist/sample/navigation/animation/AnimatedNavHostSample.kt b/sample/src/main/java/com/google/accompanist/sample/navigation/animation/AnimatedNavHostSample.kt index c47590988..4a64d6fd1 100644 --- a/sample/src/main/java/com/google/accompanist/sample/navigation/animation/AnimatedNavHostSample.kt +++ b/sample/src/main/java/com/google/accompanist/sample/navigation/animation/AnimatedNavHostSample.kt @@ -47,7 +47,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset @@ -330,7 +329,7 @@ fun NavigateButton( fun NavigateBackButton(navController: NavController) { // Use LocalLifecycleOwner.current as a proxy for the NavBackStackEntry // associated with this Composable - if (navController.currentBackStackEntry == LocalLifecycleOwner.current && + if (navController.currentBackStackEntry == androidx.lifecycle.compose.LocalLifecycleOwner.current && navController.previousBackStackEntry != null ) { Button( diff --git a/testharness/src/sharedTest/kotlin/com/google/accompanist/testharness/TestHarnessTest.kt b/testharness/src/sharedTest/kotlin/com/google/accompanist/testharness/TestHarnessTest.kt index c2d3bc004..501e00dc6 100644 --- a/testharness/src/sharedTest/kotlin/com/google/accompanist/testharness/TestHarnessTest.kt +++ b/testharness/src/sharedTest/kotlin/com/google/accompanist/testharness/TestHarnessTest.kt @@ -96,13 +96,13 @@ class TestHarnessTest { fun size_ExtremelyBig_measuredWidthIsCorrect() { var width = 0.dp composeTestRule.setContent { - TestHarness(size = DpSize(10000.dp, 10000.dp)) { - BoxOfSize(10000.dp, onWidth = { width = it }) + TestHarness(size = DpSize(5000.dp, 5000.dp)) { + BoxOfSize(5000.dp, onWidth = { width = it }) } } composeTestRule.waitForIdle() - val ratio = width / 10000.dp + val ratio = width / 5000.dp assertEquals(ratio, 1f, 0.01f) }