Merge "Finalize session when the device is already closed" into androidx-main
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/VirtualFile.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/VirtualFile.kt
index f57ca90..2d5e28c 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/VirtualFile.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/VirtualFile.kt
@@ -164,7 +164,7 @@
override fun executeCommand(block: (String) -> String): String {
val cmd = block(absolutePath)
- return trace("UserFile#executeCommand $cmd") {
+ return trace("UserFile#executeCommand $cmd".take(127)) {
DataInputStream(Runtime.getRuntime().exec(cmd).inputStream)
.bufferedReader()
.use { it.readText() }
@@ -222,7 +222,7 @@
var counterOs: CounterOutputStream? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
- trace("ShellFile#useOutputStream $cmd") {
+ trace("ShellFile#useOutputStream $cmd".take(127)) {
val (_, inDescriptor, errDescriptor) = uiAutomation.executeShellCommandRwe(cmd)
ParcelFileDescriptor.AutoCloseOutputStream(inDescriptor).use {
counterOs = CounterOutputStream(it)
@@ -231,7 +231,7 @@
checkErr(errDescriptor)
}
} else {
- trace("ShellFile#useOutputStream $cmd") {
+ trace("ShellFile#useOutputStream $cmd".take(127)) {
val (_, inDescriptor) = uiAutomation.executeShellCommandRw(cmd)
ParcelFileDescriptor.AutoCloseOutputStream(inDescriptor).use {
counterOs = CounterOutputStream(it)
@@ -268,7 +268,7 @@
override fun executeCommand(block: (String) -> String): String {
val cmd = rootState.maybeRootify(block(absolutePath))
val output =
- trace("ShellFile#executeCommand $cmd") {
+ trace("ShellFile#executeCommand $cmd".take(127)) {
uiAutomation.executeShellCommand(cmd).fullyReadInputStream()
}
return output.trim()
diff --git a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/MacrobenchmarkRule.kt b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/MacrobenchmarkRule.kt
index 7825418..da3cf49 100644
--- a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/MacrobenchmarkRule.kt
+++ b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/MacrobenchmarkRule.kt
@@ -16,7 +16,6 @@
package androidx.benchmark.macro.junit4
-import android.Manifest
import androidx.annotation.IntRange
import androidx.benchmark.Arguments
import androidx.benchmark.ExperimentalBenchmarkConfigApi
@@ -27,9 +26,7 @@
import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.macrobenchmarkWithStartupMode
import androidx.benchmark.perfetto.PerfettoConfig
-import androidx.test.rule.GrantPermissionRule
import org.junit.Assume.assumeTrue
-import org.junit.rules.RuleChain
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
@@ -226,16 +223,7 @@
measureBlock
)
- override fun apply(base: Statement, description: Description): Statement {
- // Grant external storage, as it may be needed for test output directory.
- return RuleChain.outerRule(
- GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE)
- )
- .around(::applyInternal)
- .apply(base, description)
- }
-
- private fun applyInternal(base: Statement, description: Description) =
+ override fun apply(base: Statement, description: Description): Statement =
object : Statement() {
override fun evaluate() {
assumeTrue(Arguments.RuleType.Macrobenchmark in Arguments.enabledRules)
diff --git a/biometric/biometric/src/main/res/values-fa/strings.xml b/biometric/biometric/src/main/res/values-fa/strings.xml
index 6e9482f..563ced0 100644
--- a/biometric/biometric/src/main/res/values-fa/strings.xml
+++ b/biometric/biometric/src/main/res/values-fa/strings.xml
@@ -23,7 +23,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="7520712796891883488">"اثر انگشتی ثبت نشده است."</string>
<string name="fingerprint_error_hw_not_present" msgid="6306988885793029438">"این دستگاه حسگر اثر انگشت ندارد"</string>
<string name="fingerprint_error_user_canceled" msgid="7627716295344353987">"کاربر عملیات اثر انگشت را لغو کرد"</string>
- <string name="fingerprint_error_lockout" msgid="7291787166416782245">"تعداد تلاشها بیش از حد مجاز است. لطفاً بعداً دوباره امتحان کنید."</string>
+ <string name="fingerprint_error_lockout" msgid="7291787166416782245">"تلاشهای بسیار زیاد ناموفق. لطفاً بعداً دوباره امتحان کنید."</string>
<string name="default_error_msg" msgid="4776854077120974966">"خطای ناشناس"</string>
<string name="generic_error_user_canceled" msgid="7309881387583143581">"کاربر اصالتسنجی را لغو کرد."</string>
<string name="confirm_device_credential_password" msgid="5912733858573823945">"استفاده از گذرواژه"</string>
diff --git a/buildSrc/lint.xml b/buildSrc/lint.xml
index 78d7c83..98fbf34 100644
--- a/buildSrc/lint.xml
+++ b/buildSrc/lint.xml
@@ -32,6 +32,7 @@
<ignore path="**/src/androidUnitTest/**" />
<ignore path="**/src/jvmTest/**" />
<ignore path="**/src/commonTest/**" />
+ <ignore path="**/src/nonEmulatorJvmTest/**" />
<!-- Required for AppSearch icing tests. -->
<ignore path="**/java/tests/**" />
</issue>
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/sources/ValidateMultiplatformSourceSetNaming.kt b/buildSrc/private/src/main/kotlin/androidx/build/sources/ValidateMultiplatformSourceSetNaming.kt
index 74da1e8..7e117bc 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/sources/ValidateMultiplatformSourceSetNaming.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/sources/ValidateMultiplatformSourceSetNaming.kt
@@ -91,8 +91,8 @@
project.files(
target.compilations
.filterNot { compilation ->
- // Don't enforce suffixes for test source sets.
- compilation.name == "test" || compilation.name.endsWith("Test")
+ // Don't enforce suffixes for test source sets. Names can be e.g. testOnJvm
+ compilation.name.startsWith("test") || compilation.name.endsWith("Test")
}
.flatMap { compilation -> compilation.kotlinSourceSets }
.map { kotlinSourceSet -> kotlinSourceSet.kotlin.sourceDirectories }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
index d65cef9..619a198 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
@@ -1140,6 +1140,7 @@
closeCameraDeviceOnClose = shouldCloseCameraDeviceOnClose,
finalizeSessionOnCloseBehavior = shouldFinalizeSessionOnCloseBehavior,
disableGraphLevelSurfaceTracking = shouldDisableGraphLevelSurfaceTracking,
+ enableRestartDelays = true,
)
}
}
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraControllerSimulator.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraControllerSimulator.kt
index 35386c3..a1d8f5f 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraControllerSimulator.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraControllerSimulator.kt
@@ -22,7 +22,6 @@
import androidx.camera.camera2.pipe.CameraGraph
import androidx.camera.camera2.pipe.CameraGraphId
import androidx.camera.camera2.pipe.CameraId
-import androidx.camera.camera2.pipe.CameraStatusMonitor
import androidx.camera.camera2.pipe.GraphState.GraphStateError
import androidx.camera.camera2.pipe.StreamGraph
import androidx.camera.camera2.pipe.StreamId
@@ -172,14 +171,6 @@
}
}
- override fun onCameraStatusChanged(cameraStatus: CameraStatusMonitor.CameraStatus) {
- synchronized(lock) {
- check(!closed) { "Attempted to invoke restart after close." }
- stop()
- start()
- }
- }
-
override fun close() {
synchronized(lock) {
closed = true
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraBackend.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraBackend.kt
index 56f4beb..f624b2b 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraBackend.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraBackend.kt
@@ -24,13 +24,10 @@
import androidx.camera.camera2.pipe.CameraGraphId
import androidx.camera.camera2.pipe.CameraId
import androidx.camera.camera2.pipe.CameraMetadata
-import androidx.camera.camera2.pipe.CameraStatusMonitor
import androidx.camera.camera2.pipe.StreamGraph
import androidx.camera.camera2.pipe.graph.GraphListener
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
/** The FakeCameraBackend implements [CameraBackend] and creates [CameraControllerSimulator]s. */
public class FakeCameraBackend(private val fakeCameras: Map<CameraId, CameraMetadata>) :
@@ -45,9 +42,6 @@
override val id: CameraBackendId
get() = FAKE_CAMERA_BACKEND_ID
- override val cameraStatus: Flow<CameraStatusMonitor.CameraStatus>
- get() = MutableSharedFlow()
-
override fun awaitCameraIds(): List<CameraId> = fakeCameraIds
override fun awaitConcurrentCameraIds(): Set<Set<CameraId>> = emptySet()
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraBackend.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraBackend.kt
index 2d6dbf6..19859af 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraBackend.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraBackend.kt
@@ -18,7 +18,6 @@
import androidx.annotation.RestrictTo
import androidx.camera.camera2.pipe.graph.GraphListener
import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.flow.Flow
/** This is used to uniquely identify a specific backend implementation. */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -26,30 +25,6 @@
public value class CameraBackendId(public val value: String)
/**
- * A CameraStatusMonitors monitors the status of the cameras, and emits updates when the status of
- * cameras changes, for instance when the camera access priorities have changed or when a particular
- * camera has become available.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public interface CameraStatusMonitor {
- public val cameraStatus: Flow<CameraStatus>
-
- public abstract class CameraStatus internal constructor() {
- public object CameraPrioritiesChanged : CameraStatus() {
- override fun toString(): String = "CameraPrioritiesChanged"
- }
-
- public class CameraAvailable(public val cameraId: CameraId) : CameraStatus() {
- override fun toString(): String = "CameraAvailable(camera=$cameraId)"
- }
-
- public class CameraUnavailable(public val cameraId: CameraId) : CameraStatus() {
- override fun toString(): String = "CameraUnavailable(camera=$cameraId)"
- }
- }
-}
-
-/**
* A CameraBackend is used by [CameraPipe] to abstract out the lifecycle, state, and interactions
* with a set of camera devices in a standard way.
*
@@ -67,12 +42,6 @@
public val id: CameraBackendId
/**
- * A flow of camera statuses that provide camera status updates such as when the camera access
- * priorities have changed, or a certain camera has become available.
- */
- public val cameraStatus: Flow<CameraStatusMonitor.CameraStatus>
-
- /**
* Read out a list of _openable_ [CameraId]s for this backend. The backend may be able to report
* Metadata for non-openable cameras. However, these cameras should not appear the list of
* cameras returned by [getCameraIds].
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraController.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraController.kt
index 4f39a94..7422678 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraController.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraController.kt
@@ -64,13 +64,6 @@
public fun stop()
/**
- * Restart the current session. This should basically perform stop() then start(). However, the
- * implementation should handle its internal states correctly, and only restart under the right
- * [CameraStatusMonitor.CameraStatus] and [ControllerState].
- */
- public fun onCameraStatusChanged(cameraStatus: CameraStatusMonitor.CameraStatus)
-
- /**
* Close this instance. [start] and [stop] should not be invoked, and any additional calls will
* be ignored once this method returns. Depending on implementation the underlying camera
* connection may not be terminated immediately, depending on the [CameraBackend]
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
index fd51277..4d05f58 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
@@ -399,7 +399,17 @@
* - Device(s): LEGACY camera hardware level
* - API levels: 23 or LEGACY hardware level.
*/
- val disableGraphLevelSurfaceTracking: Boolean = false
+ val disableGraphLevelSurfaceTracking: Boolean = false,
+
+ /**
+ * Flag to enable CameraGraph to restart its internal camera controller(s) with a delay. The
+ * delay might be needed during Activity switching, to allow time for the preceding Activity
+ * to close its CameraGraphs to allow for the succeeding Activity to acquire the same
+ * camera.
+ * - Bug(s): b/344752133, b/153714651
+ * - Device(s): CameraX users
+ */
+ val enableRestartDelays: Boolean = false
) {
@JvmInline
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2Backend.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2Backend.kt
index 67a2ed2..b7eb8bb 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2Backend.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2Backend.kt
@@ -24,35 +24,27 @@
import androidx.camera.camera2.pipe.CameraGraphId
import androidx.camera.camera2.pipe.CameraId
import androidx.camera.camera2.pipe.CameraMetadata
-import androidx.camera.camera2.pipe.CameraStatusMonitor.CameraStatus
import androidx.camera.camera2.pipe.StreamGraph
import androidx.camera.camera2.pipe.config.Camera2ControllerComponent
import androidx.camera.camera2.pipe.config.Camera2ControllerConfig
-import androidx.camera.camera2.pipe.core.Threads
import androidx.camera.camera2.pipe.graph.GraphListener
import androidx.camera.camera2.pipe.graph.StreamGraphImpl
import javax.inject.Inject
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.flow.Flow
/** This is the default [CameraBackend] implementation for CameraPipe based on Camera2. */
internal class Camera2Backend
@Inject
constructor(
- private val threads: Threads,
private val camera2DeviceCache: Camera2DeviceCache,
private val camera2MetadataCache: Camera2MetadataCache,
private val virtualCameraManager: VirtualCameraManager,
private val camera2CameraControllerComponent: Camera2ControllerComponent.Builder,
- private val camera2CameraStatusMonitor: Camera2CameraStatusMonitor,
) : CameraBackend {
override val id: CameraBackendId
get() = CameraBackendId("CXCP-Camera2")
- override val cameraStatus: Flow<CameraStatus>
- get() = camera2CameraStatusMonitor.cameraStatus
-
override suspend fun getCameraIds(): List<CameraId> = camera2DeviceCache.getCameraIds()
override fun awaitCameraIds(): List<CameraId>? = camera2DeviceCache.awaitCameraIds()
@@ -111,7 +103,7 @@
graphId,
graphConfig,
graphListener,
- streamGraph as StreamGraphImpl
+ streamGraph as StreamGraphImpl,
)
)
.build()
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraController.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraController.kt
index 2c7d2ec..fd75f74 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraController.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraController.kt
@@ -25,7 +25,6 @@
import androidx.camera.camera2.pipe.CameraGraph
import androidx.camera.camera2.pipe.CameraGraphId
import androidx.camera.camera2.pipe.CameraId
-import androidx.camera.camera2.pipe.CameraStatusMonitor.CameraStatus
import androidx.camera.camera2.pipe.CameraSurfaceManager
import androidx.camera.camera2.pipe.GraphState
import androidx.camera.camera2.pipe.StreamGraph
@@ -36,10 +35,13 @@
import androidx.camera.camera2.pipe.core.Threads
import androidx.camera.camera2.pipe.core.TimeSource
import androidx.camera.camera2.pipe.graph.GraphListener
+import androidx.camera.camera2.pipe.internal.CameraStatusMonitor
+import androidx.camera.camera2.pipe.internal.CameraStatusMonitor.CameraStatus
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
+import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
/**
@@ -59,6 +61,7 @@
private val threads: Threads,
private val graphConfig: CameraGraph.Config,
private val graphListener: GraphListener,
+ private val cameraStatusMonitor: CameraStatusMonitor,
private val captureSessionFactory: CaptureSessionFactory,
private val captureSequenceProcessorFactory: Camera2CaptureSequenceProcessorFactory,
private val virtualCameraManager: VirtualCameraManager,
@@ -84,126 +87,185 @@
@GuardedBy("lock") private var lastCameraError: CameraError? = null
+ @GuardedBy("lock") private var restartJob: Job? = null
+
private var currentCamera: VirtualCamera? = null
private var currentSession: CaptureSessionState? = null
private var currentSurfaceMap: Map<StreamId, Surface>? = null
private var currentCameraStateJob: Job? = null
+ private var cameraAvailabilityJob: Job? = null
+ private var cameraPrioritiesJob: Job? = null
- override fun start(): Unit =
- synchronized(lock) {
- if (controllerState == ControllerState.CLOSED) {
- Log.info { "Ignoring start(): Camera2CameraController is already closed" }
- return
- } else if (controllerState == ControllerState.STARTED) {
- Log.warn { "Ignoring start(): Camera2CameraController is already started" }
- return
- }
- lastCameraError = null
- val camera =
- virtualCameraManager.open(
- graphConfig.camera,
- graphConfig.sharedCameraIds,
- graphListener,
- ) { _ ->
- isForeground
- }
- if (camera == null) {
- Log.error {
- "Failed to start Camera2CameraController: Open request submission failed"
- }
- return
- }
-
- check(currentCamera == null)
- check(currentSession == null)
-
- currentCamera = camera
- val session =
- CaptureSessionState(
- graphListener,
- captureSessionFactory,
- captureSequenceProcessorFactory,
- cameraSurfaceManager,
- timeSource,
- graphConfig.flags,
- scope
- )
- currentSession = session
-
- val surfaces: Map<StreamId, Surface>? = currentSurfaceMap
- if (surfaces != null) {
- session.configureSurfaceMap(surfaces)
- }
-
- controllerState = ControllerState.STARTED
- Log.debug { "Started Camera2CameraController" }
- currentCameraStateJob?.cancel()
- currentCameraStateJob = scope.launch { bindSessionToCamera() }
- }
-
- override fun stop(): Unit =
- synchronized(lock) {
- if (controllerState == ControllerState.CLOSED) {
- Log.warn { "Ignoring stop(): Camera2CameraController is already closed" }
- return
- } else if (
- controllerState == ControllerState.STOPPING ||
- controllerState == ControllerState.STOPPED
- ) {
- Log.warn { "Ignoring stop(): CameraController already stopping or stopped" }
- return
- }
-
- val camera = currentCamera
- val session = currentSession
-
- currentCamera = null
- currentSession = null
-
- controllerState = ControllerState.STOPPING
- Log.debug { "Stopping Camera2CameraController" }
- disconnectSessionAndCamera(session, camera)
- }
-
- override fun onCameraStatusChanged(cameraStatus: CameraStatus): Unit =
- synchronized(lock) {
- Log.debug { "$this ($cameraId) camera status changed to $cameraStatus" }
- if (
- cameraStatus is CameraStatus.CameraAvailable ||
- cameraStatus is CameraStatus.CameraUnavailable
- ) {
- [email protected] = cameraStatus
- }
-
- var shouldRestart = false
- when (controllerState) {
- ControllerState.DISCONNECTED ->
- if (
- cameraStatus is CameraStatus.CameraAvailable ||
- cameraStatus is CameraStatus.CameraPrioritiesChanged
- ) {
- shouldRestart = true
+ init {
+ cameraAvailabilityJob =
+ scope.launch {
+ cameraStatusMonitor.cameraAvailability.collect { cameraStatus ->
+ when (cameraStatus) {
+ is CameraStatus.CameraAvailable -> {
+ check(cameraStatus.cameraId == cameraId)
+ onCameraStatusChanged(cameraStatus)
+ }
+ is CameraStatus.CameraUnavailable -> {
+ check(cameraStatus.cameraId == cameraId)
+ onCameraStatusChanged(cameraStatus)
+ }
}
- ControllerState.ERROR ->
- if (
- cameraStatus is CameraStatus.CameraAvailable &&
- lastCameraError != CameraError.ERROR_GRAPH_CONFIG
- ) {
- shouldRestart = true
- }
- }
- if (!shouldRestart) {
- Log.debug {
- "Camera status changed but not restarting: " +
- "Controller state = $controllerState, camera status = $cameraStatus."
}
- return
}
- Log.debug { "Restarting Camera2CameraController" }
- stop()
- start()
+
+ cameraPrioritiesJob =
+ scope.launch {
+ cameraStatusMonitor.cameraPriorities.collect {
+ onCameraStatusChanged(CameraStatus.CameraPrioritiesChanged)
+ }
+ }
+ }
+
+ override fun start() {
+ synchronized(lock) { startLocked() }
+ }
+
+ override fun stop() {
+ synchronized(lock) { stopLocked() }
+ }
+
+ private fun restart(delayMs: Long) {
+ synchronized(lock) {
+ restartJob?.cancel()
+ restartJob =
+ scope.launch {
+ delay(delayMs)
+ synchronized(lock) {
+ if (
+ controllerState != ControllerState.CLOSED &&
+ controllerState != ControllerState.STOPPING &&
+ controllerState != ControllerState.STOPPED
+ ) {
+ controllerState
+ stopLocked()
+ startLocked()
+ }
+ }
+ }
}
+ }
+
+ @GuardedBy("lock")
+ private fun startLocked() {
+ if (controllerState == ControllerState.CLOSED) {
+ Log.info { "Ignoring start(): Camera2CameraController is already closed" }
+ return
+ } else if (controllerState == ControllerState.STARTED) {
+ Log.warn { "Ignoring start(): Camera2CameraController is already started" }
+ return
+ }
+ lastCameraError = null
+ val camera =
+ virtualCameraManager.open(
+ graphConfig.camera,
+ graphConfig.sharedCameraIds,
+ graphListener,
+ ) { _ ->
+ isForeground
+ }
+ if (camera == null) {
+ Log.error { "Failed to start Camera2CameraController: Open request submission failed" }
+ return
+ }
+
+ check(currentCamera == null)
+ check(currentSession == null)
+
+ currentCamera = camera
+ val session =
+ CaptureSessionState(
+ graphListener,
+ captureSessionFactory,
+ captureSequenceProcessorFactory,
+ cameraSurfaceManager,
+ timeSource,
+ graphConfig.flags,
+ scope
+ )
+ currentSession = session
+
+ val surfaces: Map<StreamId, Surface>? = currentSurfaceMap
+ if (surfaces != null) {
+ session.configureSurfaceMap(surfaces)
+ }
+
+ controllerState = ControllerState.STARTED
+ Log.debug { "Started Camera2CameraController" }
+ currentCameraStateJob?.cancel()
+ currentCameraStateJob = scope.launch { bindSessionToCamera() }
+ }
+
+ @GuardedBy("lock")
+ private fun stopLocked() {
+ if (controllerState == ControllerState.CLOSED) {
+ Log.warn { "Ignoring stop(): Camera2CameraController is already closed" }
+ return
+ } else if (
+ controllerState == ControllerState.STOPPING ||
+ controllerState == ControllerState.STOPPED
+ ) {
+ Log.warn { "Ignoring stop(): CameraController already stopping or stopped" }
+ return
+ }
+
+ val camera = currentCamera
+ val session = currentSession
+
+ currentCamera = null
+ currentSession = null
+
+ controllerState = ControllerState.STOPPING
+ Log.debug { "Stopping Camera2CameraController" }
+ disconnectSessionAndCamera(session, camera)
+ }
+
+ private fun onCameraStatusChanged(cameraStatus: CameraStatus) {
+ val shouldRestart =
+ synchronized(lock) {
+ Log.debug { "$this ($cameraId) camera status changed to $cameraStatus" }
+ if (
+ cameraStatus is CameraStatus.CameraAvailable ||
+ cameraStatus is CameraStatus.CameraUnavailable
+ ) {
+ [email protected] = cameraStatus
+ }
+
+ var shouldRestart = false
+ when (controllerState) {
+ ControllerState.DISCONNECTED ->
+ if (
+ cameraStatus is CameraStatus.CameraAvailable ||
+ cameraStatus is CameraStatus.CameraPrioritiesChanged
+ ) {
+ shouldRestart = true
+ }
+ ControllerState.ERROR ->
+ if (
+ cameraStatus is CameraStatus.CameraAvailable &&
+ lastCameraError != CameraError.ERROR_GRAPH_CONFIG
+ ) {
+ shouldRestart = true
+ }
+ }
+ shouldRestart
+ }
+ if (!shouldRestart) {
+ Log.debug {
+ "Camera status changed but not restarting: " +
+ "Controller state = $controllerState, camera status = $cameraStatus."
+ }
+ return
+ }
+ Log.debug { "Restarting Camera2CameraController" }
+ val delayMs = if (graphConfig.flags.enableRestartDelays) 700L else 0L
+ restart(delayMs)
+ }
override fun close(): Unit =
synchronized(lock) {
@@ -221,6 +283,11 @@
currentCameraStateJob?.cancel()
currentCameraStateJob = null
+ cameraAvailabilityJob?.cancel()
+ cameraAvailabilityJob = null
+ cameraPrioritiesJob?.cancel()
+ cameraPrioritiesJob = null
+ cameraStatusMonitor.close()
disconnectSessionAndCamera(session, camera)
if (graphConfig.flags.closeCameraDeviceOnClose) {
@@ -347,6 +414,8 @@
}
?: run {
Log.warn { "Timeout when disconnecting session and camera for $session" }
+ Log.info { "Force finalizing current capture session" }
+ session?.finalizeSession(delayMs = 0)
graphListener.onGraphError(
GraphState.GraphStateError(
CameraError.ERROR_CAMERA_DEVICE,
@@ -358,7 +427,7 @@
}
companion object {
- private const val DISCONNECT_TIMEOUT_MS = 3_000L // 3s
+ private const val DISCONNECT_TIMEOUT_MS = 5000L // 5s
private const val MS_TO_NS = 1_000_000
}
}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraStatusMonitor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraStatusMonitor.kt
index d49ccd1..2f4c9a3 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraStatusMonitor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraStatusMonitor.kt
@@ -19,24 +19,58 @@
import android.hardware.camera2.CameraManager
import android.os.Build
import androidx.camera.camera2.pipe.CameraId
-import androidx.camera.camera2.pipe.CameraStatusMonitor
-import androidx.camera.camera2.pipe.CameraStatusMonitor.CameraStatus
import androidx.camera.camera2.pipe.core.Log
import androidx.camera.camera2.pipe.core.Threads
-import javax.inject.Inject
+import androidx.camera.camera2.pipe.internal.CameraStatusMonitor
+import androidx.camera.camera2.pipe.internal.CameraStatusMonitor.CameraStatus
import javax.inject.Provider
-import javax.inject.Singleton
+import kotlinx.atomicfu.atomic
+import kotlinx.coroutines.CoroutineName
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.onFailure
import kotlinx.coroutines.channels.trySendBlocking
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.launch
-@Singleton
-internal class Camera2CameraStatusMonitor
-@Inject
-constructor(cameraManager: Provider<CameraManager>, threads: Threads) : CameraStatusMonitor {
- override val cameraStatus = callbackFlow {
- val manager = cameraManager.get()
+internal class Camera2CameraStatusMonitor(
+ cameraManager: Provider<CameraManager>,
+ private val threads: Threads,
+ private val cameraId: CameraId,
+) : CameraStatusMonitor {
+ private val manager = cameraManager.get()
+ private val scope =
+ CoroutineScope(
+ threads.lightweightDispatcher.plus(CoroutineName("CXCP-CameraStatusMonitor"))
+ )
+
+ private val closed = atomic(false)
+
+ private val _cameraAvailability = MutableStateFlow<CameraStatus>(CameraStatus.Unknown)
+ override val cameraAvailability: StateFlow<CameraStatus> = _cameraAvailability.asStateFlow()
+
+ private val _cameraPriorities = MutableSharedFlow<Unit>()
+ override val cameraPriorities: SharedFlow<Unit> = _cameraPriorities.asSharedFlow()
+
+ private val cameraStatus = cameraStatusFlow()
+ private val cameraStatusJob =
+ scope.launch {
+ cameraStatus.collect { cameraStatus ->
+ when (cameraStatus) {
+ is CameraStatus.CameraAvailable -> _cameraAvailability.emit(cameraStatus)
+ is CameraStatus.CameraUnavailable -> _cameraAvailability.emit(cameraStatus)
+ is CameraStatus.CameraPrioritiesChanged -> _cameraPriorities.emit(Unit)
+ }
+ }
+ }
+
+ private fun cameraStatusFlow() = callbackFlow {
val availabilityCallback =
object : CameraManager.AvailabilityCallback() {
override fun onCameraAccessPrioritiesChanged() {
@@ -47,12 +81,14 @@
}
override fun onCameraAvailable(cameraId: String) {
+ if (cameraId != [email protected]) return
Log.debug { "Camera $cameraId has become available" }
trySendBlocking(CameraStatus.CameraAvailable(CameraId.fromCamera2Id(cameraId)))
.onFailure { Log.warn { "Failed to emit CameraAvailable($cameraId)" } }
}
override fun onCameraUnavailable(cameraId: String) {
+ if (cameraId != [email protected]) return
Log.debug { "Camera $cameraId has become unavailable" }
trySendBlocking(
CameraStatus.CameraUnavailable(CameraId.fromCamera2Id(cameraId))
@@ -72,4 +108,10 @@
awaitClose { manager.unregisterAvailabilityCallback(availabilityCallback) }
}
+
+ override fun close() {
+ if (closed.compareAndSet(expect = false, update = true)) {
+ cameraStatusJob.cancel()
+ }
+ }
}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt
index 8a69c5d..99a6f15 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt
@@ -367,7 +367,7 @@
}
}
- private fun finalizeSession(delayMs: Long = 0L) {
+ internal fun finalizeSession(delayMs: Long = 0L) {
if (delayMs != 0L) {
scope.launch {
Log.debug { "Finalizing $this in $delayMs ms" }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExternalRequestProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExternalRequestProcessor.kt
index 818ace31..a329318 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExternalRequestProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExternalRequestProcessor.kt
@@ -24,7 +24,6 @@
import androidx.camera.camera2.pipe.CameraGraph
import androidx.camera.camera2.pipe.CameraGraphId
import androidx.camera.camera2.pipe.CameraId
-import androidx.camera.camera2.pipe.CameraStatusMonitor
import androidx.camera.camera2.pipe.CaptureSequence
import androidx.camera.camera2.pipe.CaptureSequenceProcessor
import androidx.camera.camera2.pipe.Metadata
@@ -73,11 +72,6 @@
}
}
- override fun onCameraStatusChanged(cameraStatus: CameraStatusMonitor.CameraStatus) {
- // This is intentionally made a no-op for now as CameraPipe external doesn't support
- // camera status monitoring and camera controller restart.
- }
-
override fun close() {
// TODO: ExternalRequestProcessor will be deprecated. This is a temporary patch to allow
// graphProcessor to have a suspending shutdown function.
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/Camera2Component.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/Camera2Component.kt
index 589e929..7f403c5 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/Camera2Component.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/Camera2Component.kt
@@ -16,11 +16,11 @@
package androidx.camera.camera2.pipe.config
+import android.hardware.camera2.CameraManager
import androidx.camera.camera2.pipe.CameraBackend
import androidx.camera.camera2.pipe.CameraController
import androidx.camera.camera2.pipe.CameraGraph
import androidx.camera.camera2.pipe.CameraGraphId
-import androidx.camera.camera2.pipe.CameraStatusMonitor
import androidx.camera.camera2.pipe.StreamGraph
import androidx.camera.camera2.pipe.compat.AudioRestrictionController
import androidx.camera.camera2.pipe.compat.AudioRestrictionControllerImpl
@@ -43,10 +43,12 @@
import androidx.camera.camera2.pipe.graph.GraphListener
import androidx.camera.camera2.pipe.graph.StreamGraphImpl
import androidx.camera.camera2.pipe.internal.CameraErrorListener
+import androidx.camera.camera2.pipe.internal.CameraStatusMonitor
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.Subcomponent
+import javax.inject.Provider
import javax.inject.Scope
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
@@ -75,11 +77,6 @@
): CameraAvailabilityMonitor
@Binds
- abstract fun bindCameraStatusMonitor(
- camera2CameraStatusMonitor: Camera2CameraStatusMonitor
- ): CameraStatusMonitor
-
- @Binds
abstract fun bindCamera2DeviceCloser(
camera2CameraDeviceCloser: Camera2DeviceCloserImpl
): Camera2DeviceCloser
@@ -151,5 +148,15 @@
threads.lightweightDispatcher.plus(CoroutineName("CXCP-Camera2Controller"))
)
}
+
+ @Camera2ControllerScope
+ @Provides
+ fun provideCameraStatusMonitor(
+ cameraManager: Provider<CameraManager>,
+ threads: Threads,
+ graphConfig: CameraGraph.Config
+ ): CameraStatusMonitor {
+ return Camera2CameraStatusMonitor(cameraManager, threads, graphConfig.camera)
+ }
}
}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/CameraPipeComponent.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/CameraPipeComponent.kt
index e337f8e..e0d3b49 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/CameraPipeComponent.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/CameraPipeComponent.kt
@@ -54,8 +54,6 @@
/** Qualifier for requesting the CameraPipe scoped Context object */
@Qualifier internal annotation class CameraPipeContext
-@Qualifier internal annotation class ForGraphLifecycleManager
-
@Singleton
@Component(
modules =
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraGraphComponent.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraGraphComponent.kt
index 4035977..7089a3b 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraGraphComponent.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraGraphComponent.kt
@@ -26,7 +26,6 @@
import androidx.camera.camera2.pipe.CameraGraphId
import androidx.camera.camera2.pipe.CameraId
import androidx.camera.camera2.pipe.CameraMetadata
-import androidx.camera.camera2.pipe.CameraStatusMonitor
import androidx.camera.camera2.pipe.RequestProcessor
import androidx.camera.camera2.pipe.StreamGraph
import androidx.camera.camera2.pipe.compat.ExternalCameraController
@@ -35,8 +34,6 @@
import dagger.Provides
import dagger.Subcomponent
import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
@CameraGraphScope
@Subcomponent(modules = [SharedCameraGraphModules::class, ExternalCameraGraphConfigModule::class])
@@ -62,9 +59,6 @@
override val id: CameraBackendId
get() = CameraBackendId("External")
- override val cameraStatus: Flow<CameraStatusMonitor.CameraStatus>
- get() = MutableSharedFlow()
-
override suspend fun getCameraIds(): List<CameraId>? {
throwUnsupportedOperationException()
}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ThreadConfigModule.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ThreadConfigModule.kt
index 1e3d6cf..ec923aa 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ThreadConfigModule.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ThreadConfigModule.kt
@@ -43,7 +43,7 @@
// Lightweight executors are for CPU bound work that should take less than ~10ms to operate and
// do not block the calling thread.
private val lightweightThreadCount: Int =
- maxOf(2, Runtime.getRuntime().availableProcessors() - 2)
+ maxOf(4, Runtime.getRuntime().availableProcessors() - 2)
// Background thread count is for operations that are not latency sensitive and may take more
// than a few milliseconds to run.
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphImpl.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphImpl.kt
index ad0de5a..e7ee3477 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphImpl.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphImpl.kt
@@ -19,7 +19,6 @@
import android.os.Build
import android.view.Surface
import androidx.camera.camera2.pipe.AudioRestrictionMode
-import androidx.camera.camera2.pipe.CameraBackend
import androidx.camera.camera2.pipe.CameraController
import androidx.camera.camera2.pipe.CameraGraph
import androidx.camera.camera2.pipe.CameraGraphId
@@ -37,7 +36,6 @@
import androidx.camera.camera2.pipe.core.tryAcquireToken
import androidx.camera.camera2.pipe.internal.FrameCaptureQueue
import androidx.camera.camera2.pipe.internal.FrameDistributor
-import androidx.camera.camera2.pipe.internal.GraphLifecycleManager
import javax.inject.Inject
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CoroutineScope
@@ -56,12 +54,10 @@
constructor(
graphConfig: CameraGraph.Config,
metadata: CameraMetadata,
- private val graphLifecycleManager: GraphLifecycleManager,
private val graphProcessor: GraphProcessor,
private val graphListener: GraphListener,
private val streamGraph: StreamGraphImpl,
private val surfaceGraph: SurfaceGraph,
- private val cameraBackend: CameraBackend,
private val cameraController: CameraController,
private val graphState3A: GraphState3A,
private val listener3A: Listener3A,
@@ -135,7 +131,7 @@
Debug.traceStart { "$this#start" }
Log.info { "Starting $this" }
graphListener.onGraphStarting()
- graphLifecycleManager.monitorAndStart(cameraBackend, cameraController)
+ cameraController.start()
Debug.traceStop()
}
@@ -145,7 +141,7 @@
Debug.traceStart { "$this#stop" }
Log.info { "Stopping $this" }
graphListener.onGraphStopping()
- graphLifecycleManager.monitorAndStop(cameraBackend, cameraController)
+ cameraController.stop()
Debug.traceStop()
}
@@ -211,7 +207,7 @@
Debug.traceStart { "$this#close" }
Log.info { "Closing $this" }
graphProcessor.close()
- graphLifecycleManager.monitorAndClose(cameraBackend, cameraController)
+ cameraController.close()
frameDistributor.close()
frameCaptureQueue.close()
surfaceGraph.close()
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/CameraStatusMonitor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/CameraStatusMonitor.kt
new file mode 100644
index 0000000..869f506
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/CameraStatusMonitor.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2024 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.camera.camera2.pipe.internal
+
+import androidx.annotation.RestrictTo
+import androidx.camera.camera2.pipe.CameraId
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * A CameraStatusMonitor monitors the status of the cameras, and emits updates when the status of
+ * cameras changes, for instance when the camera access priorities have changed or when a particular
+ * camera has become available.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+internal interface CameraStatusMonitor : AutoCloseable {
+
+ /** Gets the state flow of the availability of the current camera. */
+ val cameraAvailability: StateFlow<CameraStatus>
+
+ /** A shared flow that emits when camera access priorities have changed. */
+ val cameraPriorities: SharedFlow<Unit>
+
+ abstract class CameraStatus internal constructor() {
+ object Unknown : CameraStatus() {
+ override fun toString(): String = "UnknownCameraStatus"
+ }
+
+ object CameraPrioritiesChanged : CameraStatus() {
+ override fun toString(): String = "CameraPrioritiesChanged"
+ }
+
+ class CameraAvailable(val cameraId: CameraId) : CameraStatus() {
+ override fun toString(): String = "CameraAvailable(camera=$cameraId)"
+ }
+
+ class CameraUnavailable(val cameraId: CameraId) : CameraStatus() {
+ override fun toString(): String = "CameraUnavailable(camera=$cameraId)"
+ }
+ }
+}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/GraphLifecycleManager.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/GraphLifecycleManager.kt
deleted file mode 100644
index f56d3aa..0000000
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/GraphLifecycleManager.kt
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright 2023 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.camera.camera2.pipe.internal
-
-import androidx.annotation.GuardedBy
-import androidx.camera.camera2.pipe.CameraBackend
-import androidx.camera.camera2.pipe.CameraBackendId
-import androidx.camera.camera2.pipe.CameraController
-import androidx.camera.camera2.pipe.CameraId
-import androidx.camera.camera2.pipe.CameraStatusMonitor
-import androidx.camera.camera2.pipe.CameraStatusMonitor.CameraStatus
-import androidx.camera.camera2.pipe.core.Threads
-import javax.inject.Inject
-import javax.inject.Singleton
-import kotlinx.coroutines.CoroutineName
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
-
-/**
- * GraphLifecycleManager is a CameraPipe-level lifecycle manager that does the following:
- * - Oversees and executes the operations of [CameraController]`s. This means it will make sure the
- * operations are atomic, and executed based on permissible state transitions.
- * - Subscribe to [CameraStatusMonitor]s for camera status changes, basically “can attempt to
- * restart signals”, from the respective camera backends, and then only restart
- * [CameraController]s when the conditions are right.
- * - Once we've determined that we can restart [CameraController]s, select the “suitable”
- * [CameraController] to restart.
- */
-@Singleton
-internal class GraphLifecycleManager @Inject constructor(val threads: Threads) {
- private val lock = Any()
-
- private val scope =
- CoroutineScope(
- threads.lightweightDispatcher.plus(CoroutineName("CXCP-GraphLifecycleManager"))
- )
-
- @GuardedBy("lock")
- private val backendControllerMap =
- mutableMapOf<CameraBackendId, LinkedHashSet<CameraController>>()
-
- @GuardedBy("lock")
- private val backendCameraStatusMap =
- mutableMapOf<CameraBackendId, MutableMap<CameraId, CameraStatus>>()
-
- @GuardedBy("lock") private val backendStatusCollectJobMap = mutableMapOf<CameraBackendId, Job>()
-
- internal fun monitorAndStart(cameraBackend: CameraBackend, cameraController: CameraController) =
- synchronized(lock) {
- startMonitoring(cameraBackend, cameraController)
- cameraController.start()
- }
-
- internal fun monitorAndStop(cameraBackend: CameraBackend, cameraController: CameraController) =
- synchronized(lock) {
- cameraController.stop()
- stopMonitoring(cameraBackend, cameraController)
- }
-
- internal fun monitorAndClose(cameraBackend: CameraBackend, cameraController: CameraController) =
- synchronized(lock) {
- cameraController.close()
- stopMonitoring(cameraBackend, cameraController)
- }
-
- @GuardedBy("lock")
- private fun startMonitoring(cameraBackend: CameraBackend, cameraController: CameraController) {
- // Update this camera controller with the latest camera status, if exist.
- backendCameraStatusMap[cameraBackend.id]?.get(cameraController.cameraId)?.let { status ->
- cameraController.onCameraStatusChanged(status)
- }
-
- if (backendControllerMap.containsKey(cameraBackend.id)) {
- backendControllerMap[cameraBackend.id]?.add(cameraController)
- return
- }
- backendControllerMap[cameraBackend.id] = linkedSetOf(cameraController)
- backendStatusCollectJobMap[cameraBackend.id] =
- scope.launch {
- cameraBackend.cameraStatus.collect { cameraStatus ->
- when (cameraStatus) {
- is CameraStatus.CameraPrioritiesChanged ->
- onCameraStatusChanged(cameraBackend, cameraStatus)
- is CameraStatus.CameraAvailable ->
- onCameraStatusChanged(
- cameraBackend,
- cameraStatus,
- cameraStatus.cameraId,
- )
- is CameraStatus.CameraUnavailable ->
- onCameraStatusChanged(
- cameraBackend,
- cameraStatus,
- cameraStatus.cameraId,
- )
- }
- }
- }
- }
-
- @GuardedBy("lock")
- private fun stopMonitoring(cameraBackend: CameraBackend, cameraController: CameraController) {
- if (backendControllerMap.containsKey(cameraBackend.id)) {
- val controllerSet = backendControllerMap[cameraBackend.id]
- controllerSet?.remove(cameraController)
- if (controllerSet?.size == 0) {
- backendControllerMap.remove(cameraBackend.id)
- backendStatusCollectJobMap[cameraBackend.id]?.cancel()
- backendStatusCollectJobMap.remove(cameraBackend.id)
- }
- }
- }
-
- private fun onCameraStatusChanged(
- cameraBackend: CameraBackend,
- cameraStatus: CameraStatus,
- cameraId: CameraId? = null,
- ) =
- synchronized(lock) {
- if (cameraId != null) {
- val cameraStatusMap =
- backendCameraStatusMap.getOrPut(cameraBackend.id) { mutableMapOf() }
- cameraStatusMap[cameraId] = cameraStatus
- }
- // Restart the last CameraController being tracked in each backend. The last
- // CameraController would be the latest one being tracked, and should thus take priority
- // over previous CameraControllers.
- backendControllerMap[cameraBackend.id]
- ?.findLast {
- if (cameraId != null) {
- it.cameraId == cameraId
- } else {
- true
- }
- }
- ?.onCameraStatusChanged(cameraStatus)
- }
-}
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/CameraGraphImplTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/CameraGraphImplTest.kt
index fb91e7d..8fd32cc 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/CameraGraphImplTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/CameraGraphImplTest.kt
@@ -34,7 +34,6 @@
import androidx.camera.camera2.pipe.internal.CameraGraphParametersImpl
import androidx.camera.camera2.pipe.internal.FrameCaptureQueue
import androidx.camera.camera2.pipe.internal.FrameDistributor
-import androidx.camera.camera2.pipe.internal.GraphLifecycleManager
import androidx.camera.camera2.pipe.internal.ImageSourceMap
import androidx.camera.camera2.pipe.media.ImageReaderImageSources
import androidx.camera.camera2.pipe.testing.CameraControllerSimulator
@@ -109,7 +108,6 @@
threads
)
private val cameraContext = CameraBackendsImpl.CameraBackendContext(context, threads, backends)
- private val graphLifecycleManager = GraphLifecycleManager(threads)
private val imageSources = ImageReaderImageSources(threads)
private val frameCaptureQueue = FrameCaptureQueue()
private val cameraController =
@@ -134,12 +132,10 @@
CameraGraphImpl(
graphConfig,
metadata,
- graphLifecycleManager,
fakeGraphProcessor,
fakeGraphProcessor,
streamGraph,
surfaceGraph,
- backend,
cameraController,
GraphState3A(),
Listener3A(),
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraController.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraController.kt
index 1319fc0..f3a3e4a 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraController.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraController.kt
@@ -20,7 +20,6 @@
import androidx.camera.camera2.pipe.CameraController
import androidx.camera.camera2.pipe.CameraGraphId
import androidx.camera.camera2.pipe.CameraId
-import androidx.camera.camera2.pipe.CameraStatusMonitor
import androidx.camera.camera2.pipe.StreamGraph
import androidx.camera.camera2.pipe.StreamId
@@ -41,11 +40,6 @@
started = false
}
- override fun onCameraStatusChanged(cameraStatus: CameraStatusMonitor.CameraStatus) {
- stop()
- start()
- }
-
override fun close() {
closed = true
started = false
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCameraControl.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCameraControl.java
index 947f25b..19bb708 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCameraControl.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCameraControl.java
@@ -15,6 +15,7 @@
*/
package androidx.camera.core.streamsharing;
+import static androidx.camera.core.ImageCapture.FLASH_TYPE_USE_TORCH_AS_FLASH;
import static androidx.core.util.Preconditions.checkArgument;
import static java.util.Collections.singletonList;
@@ -58,8 +59,14 @@
@ImageCapture.FlashType int flashType) {
checkArgument(captureConfigs.size() == 1, "Only support one capture config.");
+ // FLASH_TYPE_USE_TORCH_AS_FLASH is used to ensure the flash is always on when capturing.
+ // Since we are using JPEG snapshot here, there is no way for the framework to know exactly
+ // when capture is invoked and thus torch as flash workaround is required. Note that this
+ // becomes an issue only when TEMPLATE_PREVIEW is used, usually due to quirk like
+ // PreviewUnderExposureQuirk right now, since TEMPLATE_RECORD would use torch as flash
+ // capture workaround anyway.
ListenableFuture<CameraCapturePipeline> capturePipeline = getCameraCapturePipelineAsync(
- captureMode, flashType);
+ captureMode, FLASH_TYPE_USE_TORCH_AS_FLASH);
ListenableFuture<Void> captureFuture = FutureChain.from(
capturePipeline
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
index ed807fd..7acbea7 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
@@ -793,7 +793,8 @@
@Test
fun updateVideoUsage_whenRecordingStartedPausedResumedStopped(): Unit = runBlocking {
implName.ignoreTestForCameraPipe(
- "TODO: b/339615736 - Enable when implemented at camera-pipe"
+ "TODO: b/339615736 - Enable when implemented at camera-pipe",
+ evenInLab = true,
)
checkAndBindUseCases(videoCapture, preview)
@@ -832,7 +833,8 @@
assumeStopCodecAfterSurfaceRemovalCrashMediaServerQuirk()
implName.ignoreTestForCameraPipe(
- "TODO: b/339615736 - Enable when implemented at camera-pipe"
+ "TODO: b/339615736 - Enable when implemented at camera-pipe",
+ evenInLab = true,
)
checkAndBindUseCases(preview, videoCapture)
@@ -864,7 +866,8 @@
assumeStopCodecAfterSurfaceRemovalCrashMediaServerQuirk()
implName.ignoreTestForCameraPipe(
- "TODO: b/339615736 - Enable when implemented at camera-pipe"
+ "TODO: b/339615736 - Enable when implemented at camera-pipe",
+ evenInLab = true,
)
checkAndBindUseCases(preview, videoCapture)
@@ -888,7 +891,8 @@
)
implName.ignoreTestForCameraPipe(
- "TODO: b/339615736 - Enable when implemented at camera-pipe"
+ "TODO: b/339615736 - Enable when implemented at camera-pipe",
+ evenInLab = true,
)
checkAndBindUseCases(preview, videoCapture)
@@ -922,7 +926,8 @@
)
implName.ignoreTestForCameraPipe(
- "TODO: b/339615736 - Enable when implemented at camera-pipe"
+ "TODO: b/339615736 - Enable when implemented at camera-pipe",
+ evenInLab = true,
)
checkAndBindUseCases(preview, videoCapture)
@@ -956,7 +961,8 @@
)
implName.ignoreTestForCameraPipe(
- "TODO: b/339615736 - Enable when implemented at camera-pipe"
+ "TODO: b/339615736 - Enable when implemented at camera-pipe",
+ evenInLab = true,
)
checkAndBindUseCases(preview, videoCapture)
diff --git a/camera/camera-view/api/current.txt b/camera/camera-view/api/current.txt
index 3b66417..d2fd422 100644
--- a/camera/camera-view/api/current.txt
+++ b/camera/camera-view/api/current.txt
@@ -85,9 +85,6 @@
field @Deprecated public static final int UNASSIGNED_ASPECT_RATIO = -1; // 0xffffffff
}
- @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalPreviewViewScreenFlash {
- }
-
public final class LifecycleCameraController extends androidx.camera.view.CameraController {
ctor public LifecycleCameraController(android.content.Context);
method @MainThread public void bindToLifecycle(androidx.lifecycle.LifecycleOwner);
@@ -106,7 +103,7 @@
method @SuppressCompatibility public androidx.camera.view.transform.OutputTransform? getOutputTransform();
method public androidx.lifecycle.LiveData<androidx.camera.view.PreviewView.StreamState!> getPreviewStreamState();
method @UiThread public androidx.camera.view.PreviewView.ScaleType getScaleType();
- method @SuppressCompatibility @UiThread @androidx.camera.view.ExperimentalPreviewViewScreenFlash public androidx.camera.core.ImageCapture.ScreenFlash? getScreenFlash();
+ method @UiThread public androidx.camera.core.ImageCapture.ScreenFlash? getScreenFlash();
method @UiThread public android.graphics.Matrix? getSensorToViewTransform();
method @UiThread public androidx.camera.core.Preview.SurfaceProvider getSurfaceProvider();
method @UiThread public androidx.camera.core.ViewPort? getViewPort();
@@ -114,7 +111,7 @@
method @UiThread public void setController(androidx.camera.view.CameraController?);
method @UiThread public void setImplementationMode(androidx.camera.view.PreviewView.ImplementationMode);
method @UiThread public void setScaleType(androidx.camera.view.PreviewView.ScaleType);
- method @SuppressCompatibility @androidx.camera.view.ExperimentalPreviewViewScreenFlash public void setScreenFlashOverlayColor(@ColorInt int);
+ method public void setScreenFlashOverlayColor(@ColorInt int);
method @UiThread public void setScreenFlashWindow(android.view.Window?);
}
diff --git a/camera/camera-view/api/restricted_current.txt b/camera/camera-view/api/restricted_current.txt
index 3b66417..d2fd422 100644
--- a/camera/camera-view/api/restricted_current.txt
+++ b/camera/camera-view/api/restricted_current.txt
@@ -85,9 +85,6 @@
field @Deprecated public static final int UNASSIGNED_ASPECT_RATIO = -1; // 0xffffffff
}
- @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalPreviewViewScreenFlash {
- }
-
public final class LifecycleCameraController extends androidx.camera.view.CameraController {
ctor public LifecycleCameraController(android.content.Context);
method @MainThread public void bindToLifecycle(androidx.lifecycle.LifecycleOwner);
@@ -106,7 +103,7 @@
method @SuppressCompatibility public androidx.camera.view.transform.OutputTransform? getOutputTransform();
method public androidx.lifecycle.LiveData<androidx.camera.view.PreviewView.StreamState!> getPreviewStreamState();
method @UiThread public androidx.camera.view.PreviewView.ScaleType getScaleType();
- method @SuppressCompatibility @UiThread @androidx.camera.view.ExperimentalPreviewViewScreenFlash public androidx.camera.core.ImageCapture.ScreenFlash? getScreenFlash();
+ method @UiThread public androidx.camera.core.ImageCapture.ScreenFlash? getScreenFlash();
method @UiThread public android.graphics.Matrix? getSensorToViewTransform();
method @UiThread public androidx.camera.core.Preview.SurfaceProvider getSurfaceProvider();
method @UiThread public androidx.camera.core.ViewPort? getViewPort();
@@ -114,7 +111,7 @@
method @UiThread public void setController(androidx.camera.view.CameraController?);
method @UiThread public void setImplementationMode(androidx.camera.view.PreviewView.ImplementationMode);
method @UiThread public void setScaleType(androidx.camera.view.PreviewView.ScaleType);
- method @SuppressCompatibility @androidx.camera.view.ExperimentalPreviewViewScreenFlash public void setScreenFlashOverlayColor(@ColorInt int);
+ method public void setScreenFlashOverlayColor(@ColorInt int);
method @UiThread public void setScreenFlashWindow(android.view.Window?);
}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/ExperimentalPreviewViewScreenFlash.java b/camera/camera-view/src/main/java/androidx/camera/view/ExperimentalPreviewViewScreenFlash.java
deleted file mode 100644
index df9117e..0000000
--- a/camera/camera-view/src/main/java/androidx/camera/view/ExperimentalPreviewViewScreenFlash.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2024 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.camera.view;
-
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
-import androidx.annotation.RequiresOptIn;
-
-import java.lang.annotation.Retention;
-
-/**
- * Denotes that the annotated API is designed to be experimental for the screen flash feature and
- * may change in a future release.
- */
-@Retention(CLASS)
-@RequiresOptIn
-public @interface ExperimentalPreviewViewScreenFlash {
-}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java b/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java
index d128e54..5af22bf 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java
@@ -1179,7 +1179,6 @@
* @see ScreenFlashView#getScreenFlash()
* @see ImageCapture#FLASH_MODE_SCREEN
*/
- @ExperimentalPreviewViewScreenFlash
@UiThread
@Nullable
public ImageCapture.ScreenFlash getScreenFlash() {
@@ -1194,7 +1193,6 @@
* @see #getScreenFlash()
* @see ImageCapture#FLASH_MODE_SCREEN
*/
- @ExperimentalPreviewViewScreenFlash
public void setScreenFlashOverlayColor(@ColorInt int color) {
mScreenFlashView.setBackgroundColor(color);
}
diff --git a/camera/integration-tests/camerapipetestapp/src/main/java/androidx/camera/integration/camera2/pipe/CameraPipeActivity.kt b/camera/integration-tests/camerapipetestapp/src/main/java/androidx/camera/integration/camera2/pipe/CameraPipeActivity.kt
index ece3c3d..75727d7 100644
--- a/camera/integration-tests/camerapipetestapp/src/main/java/androidx/camera/integration/camera2/pipe/CameraPipeActivity.kt
+++ b/camera/integration-tests/camerapipetestapp/src/main/java/androidx/camera/integration/camera2/pipe/CameraPipeActivity.kt
@@ -137,7 +137,7 @@
var cameras = currentCameras
cameras?.let {
for (camera in it) {
- camera.stop()
+ camera.close()
}
}
Trace.endSection()
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FlashTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FlashTest.kt
index 527aeae..2e8fc9a 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FlashTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FlashTest.kt
@@ -209,13 +209,16 @@
fun flashEnabledInRequest_whenCapturedWithFlashOnAndSharedEffect() {
verifyRequestAeOrFlashModeForFlashModeCapture(
ImageCapture.FLASH_MODE_ON,
- addSharedEffect = true
+ addSharedEffect = true,
+ // In this test, torch as flash workaround should always be used
+ expectedAeMode = CONTROL_AE_MODE_ON,
)
}
private fun verifyRequestAeOrFlashModeForFlashModeCapture(
@ImageCapture.FlashMode flashMode: Int,
addSharedEffect: Boolean = false,
+ expectedAeMode: Int? = null,
) {
Assume.assumeFalse(
"Cuttlefish API 29 has AE mode availability issue for flash enabled modes." +
@@ -234,11 +237,12 @@
@Volatile var isAeModeExpected = true
private val expectedAeMode =
- when (flashMode) {
- ImageCapture.FLASH_MODE_ON -> CONTROL_AE_MODE_ON_ALWAYS_FLASH
- ImageCapture.FLASH_MODE_AUTO -> CONTROL_AE_MODE_ON_AUTO_FLASH
- else -> CONTROL_AE_MODE_ON
- }
+ expectedAeMode
+ ?: when (flashMode) {
+ ImageCapture.FLASH_MODE_ON -> CONTROL_AE_MODE_ON_ALWAYS_FLASH
+ ImageCapture.FLASH_MODE_AUTO -> CONTROL_AE_MODE_ON_AUTO_FLASH
+ else -> CONTROL_AE_MODE_ON
+ }
override fun onCaptureCompleted(
session: CameraCaptureSession,
@@ -251,7 +255,7 @@
isFlashModeSet = true
}
- if (request[CONTROL_AE_MODE] != expectedAeMode) {
+ if (request[CONTROL_AE_MODE] != this.expectedAeMode) {
isAeModeExpected = false
}
}
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsActivityTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsActivityTest.kt
index c933712..7ef19fa 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsActivityTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsActivityTest.kt
@@ -42,6 +42,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
import androidx.test.uiautomator.UiDevice
+import androidx.testutils.withActivity
import org.junit.After
import org.junit.Assume
import org.junit.Assume.assumeTrue
@@ -200,4 +201,21 @@
return activityScenario
}
+
+ @Test
+ fun checkPreviewUpdated_afterSwitchCamera() {
+ val activityScenario =
+ launchCamera2ExtensionsActivityAndWaitForCaptureSessionConfigured(config)
+ with(activityScenario) { // Launches activity
+ use { // Ensures that ActivityScenario is cleaned up properly
+ // Waits for preview to receive enough frames for its IdlingResource to idle.
+ waitForPreviewIdle()
+
+ withActivity { switchCamera() }
+
+ // Waits for preview to receive enough frames again after switching camera
+ waitForPreviewIdle()
+ }
+ }
+ }
}
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Camera2ExtensionsActivity.kt b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Camera2ExtensionsActivity.kt
index 5ca6d34..c9c78a7 100644
--- a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Camera2ExtensionsActivity.kt
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Camera2ExtensionsActivity.kt
@@ -620,24 +620,7 @@
}
val cameraSwitchButton = findViewById<Button>(R.id.Switch)
- cameraSwitchButton.setOnClickListener {
- val newCameraId = if (currentCameraId == backCameraId) frontCameraId else backCameraId
-
- if (!isCameraSupportExtensions(newCameraId)) {
- Toast.makeText(
- this,
- "Camera of the other lens facing doesn't support Camera2 extensions.",
- Toast.LENGTH_SHORT
- )
- .show()
- return@setOnClickListener
- }
-
- enableUiControl(false)
- currentCameraId = newCameraId
- restartCamera = true
- closeCaptureSessionAndCameraAsync()
- }
+ cameraSwitchButton.setOnClickListener { switchCamera() }
val captureButton = findViewById<Button>(R.id.Picture)
captureButton.setOnClickListener {
@@ -647,6 +630,26 @@
}
}
+ @VisibleForTesting
+ fun switchCamera() {
+ val newCameraId = if (currentCameraId == backCameraId) frontCameraId else backCameraId
+
+ if (!isCameraSupportExtensions(newCameraId)) {
+ Toast.makeText(
+ this,
+ "Camera of the other lens facing doesn't support Camera2 extensions.",
+ Toast.LENGTH_SHORT
+ )
+ .show()
+ return
+ }
+
+ enableUiControl(false)
+ currentCameraId = newCameraId
+ restartCamera = true
+ closeCaptureSessionAndCameraAsync()
+ }
+
override fun onStart() {
super.onStart()
Log.d(TAG, "onStart()")
@@ -769,7 +772,7 @@
if (!oldCaptureSessionClosedDeferred.isCompleted) {
oldCaptureSessionClosedDeferred.complete(Unit)
}
- if (!keepCamera && synchronized(lock) { activityStopped }) {
+ if (!keepCamera || synchronized(lock) { activityStopped }) {
Log.d(TAG, "Close camera++")
cameraDevice?.close()
cameraDevice = null
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml
index 400f759..d70e320 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml
@@ -81,8 +81,8 @@
<string name="dial" msgid="3145707439707628311">"Marca"</string>
<string name="address" msgid="9010635942573581302">"Adreça"</string>
<string name="phone" msgid="2504766809811627577">"Telèfon"</string>
- <string name="fail_start_nav" msgid="6921321606009212189">"S\'ha produït un error en iniciar la navegació"</string>
- <string name="fail_start_dialer" msgid="1471602619507306261">"S\'ha produït un error en iniciar el telèfon"</string>
+ <string name="fail_start_nav" msgid="6921321606009212189">"Hi ha hagut un error en iniciar la navegació"</string>
+ <string name="fail_start_dialer" msgid="1471602619507306261">"Hi ha hagut un error en iniciar el telèfon"</string>
<string name="car_hardware_demo_title" msgid="3679106197233262689">"Demostració del maquinari del cotxe"</string>
<string name="car_hardware_info" msgid="1244783247616395012">"Informació sobre el maquinari del cotxe"</string>
<string name="model_info" msgid="494224423025683030">"Informació del model"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml
index e50341f..0d07cf6 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml
@@ -94,7 +94,7 @@
<string name="no_energy_profile_permission" msgid="4662285713731308888">"اجازه نمایه سوخت اعطا نشده است."</string>
<string name="fuel_types" msgid="6811375173343218212">"انواع سوخت"</string>
<string name="unavailable" msgid="3636401138255192934">"دردسترس نیست"</string>
- <string name="ev_connector_types" msgid="735458637011996125">"انواع رابطهای خودروی برقی"</string>
+ <string name="ev_connector_types" msgid="735458637011996125">"انواع رابطهای خودرو برقی"</string>
<string name="example_title" msgid="530257630320010494">"نمونه %d"</string>
<string name="example_1_text" msgid="8631503055894800688">"رنگ این نوشتار "<annotation color="red">"قرمز"</annotation>" است"</string>
<string name="example_2_text" msgid="1359373957397219102">"رنگ این نوشتار "<annotation color="green">"سبز"</annotation>" است"</string>
@@ -181,7 +181,7 @@
<string name="no_energy_level_permission" msgid="1684773185095107825">"اجازه میزان سوخت اعطا نشده است."</string>
<string name="no_speed_permission" msgid="5812532480922675390">"اجازه سرعت اعطا نشده است."</string>
<string name="no_mileage_permission" msgid="4074779840599589847">"اجازه مسافت طیشده اعطا نشده است."</string>
- <string name="no_ev_status_permission" msgid="933075402821938973">"اجازه وضعیت خودروی برقی اعطا نشده است."</string>
+ <string name="no_ev_status_permission" msgid="933075402821938973">"اجازه وضعیت خودرو برقی اعطا نشده است."</string>
<string name="no_accelerometer_permission" msgid="896914448469117234">"اجازه شتابسنج اعطا نشده است."</string>
<string name="no_gyroscope_permission" msgid="665293140266771569">"اجازه ژیروسکوپ اعطا نشده است."</string>
<string name="no_compass_permission" msgid="5162304489577567125">"اجازه قطبنما اعطا نشده است."</string>
@@ -190,7 +190,7 @@
<string name="fetch_energy_level" msgid="1773415471137542832">"درحال واکشی میزان سوخت."</string>
<string name="fetch_speed" msgid="7333830984597189627">"درحال واکشی سرعت."</string>
<string name="fetch_mileage" msgid="7490131687788025123">"درحال واکشی مسافت طیشده."</string>
- <string name="fetch_ev_status" msgid="2798910410830567052">"درحال واکشی وضعیت خودروی برقی."</string>
+ <string name="fetch_ev_status" msgid="2798910410830567052">"درحال واکشی وضعیت خودرو برقی."</string>
<string name="fetch_accelerometer" msgid="697750041126810911">"درحال واکشی شتابسنج."</string>
<string name="fetch_gyroscope" msgid="7153155318827188539">"درحال واکشی ژیروسکوپ."</string>
<string name="fetch_compass" msgid="7316188117590056717">"درحال واکشی قطبنما."</string>
@@ -204,8 +204,8 @@
<string name="raw_speed" msgid="7295910214088983967">"سرعت اولیه"</string>
<string name="unit" msgid="7697521583928135171">"واحد"</string>
<string name="odometer" msgid="3925174645651546591">"مسافتشمار"</string>
- <string name="ev_connected" msgid="2277845607662494696">"درگاه شارژ خودروی برقی متصل شد"</string>
- <string name="ev_open" msgid="4916704450914519643">"درگاه شارژ خودروی برقی باز است"</string>
+ <string name="ev_connected" msgid="2277845607662494696">"درگاه شارژ خودرو برقی متصل شد"</string>
+ <string name="ev_open" msgid="4916704450914519643">"درگاه شارژ خودرو برقی باز است"</string>
<string name="accelerometer" msgid="2084026313768299185">"شتابسنج"</string>
<string name="gyroscope" msgid="3428075828014504651">"ژیروسکوپ"</string>
<string name="compass" msgid="7037367764762441245">"قطبنما"</string>
diff --git a/compose/animation/animation-core/build.gradle b/compose/animation/animation-core/build.gradle
index fac6444..4a6cdf1 100644
--- a/compose/animation/animation-core/build.gradle
+++ b/compose/animation/animation-core/build.gradle
@@ -29,12 +29,30 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
id("AndroidXComposePlugin")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.compose.animation.core"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ optimization {
+ it.consumerKeepRules.publish = true
+ it.consumerKeepRules.files.add(
+ new File(project.projectDir, "proguard-rules.pro")
+ )
+ }
+
+ compileSdk = 35
+ aarMetadata.minCompileSdk = 35
+ }
jvmStubs()
linuxX64Stubs()
@@ -132,10 +150,3 @@
kotlinTarget = KotlinTarget.KOTLIN_1_9
}
-android {
- compileSdk 35
- namespace "androidx.compose.animation.core"
- buildTypes.configureEach {
- consumerProguardFiles("proguard-rules.pro")
- }
-}
diff --git a/compose/animation/animation/build.gradle b/compose/animation/animation/build.gradle
index fdc6664..56fd517 100644
--- a/compose/animation/animation/build.gradle
+++ b/compose/animation/animation/build.gradle
@@ -29,12 +29,24 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
id("AndroidXComposePlugin")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.compose.animation"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+
+ compileSdk = 35
+ aarMetadata.minCompileSdk = 35
+ }
jvmStubs()
linuxX64Stubs()
@@ -130,7 +142,3 @@
kotlinTarget = KotlinTarget.KOTLIN_1_9
}
-android {
- compileSdk 35
- namespace "androidx.compose.animation"
-}
diff --git a/compose/foundation/foundation-layout/build.gradle b/compose/foundation/foundation-layout/build.gradle
index a90bb95..54dd48b 100644
--- a/compose/foundation/foundation-layout/build.gradle
+++ b/compose/foundation/foundation-layout/build.gradle
@@ -28,12 +28,31 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
id("AndroidXComposePlugin")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.compose.foundation.layout"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+
+ compileSdk = 35
+ aarMetadata.minCompileSdk = 35
+
+ optimization {
+ it.consumerKeepRules.publish = true
+ it.consumerKeepRules.files.add(
+ new File(project.projectDir, "proguard-rules.pro")
+ )
+ }
+ }
jvmStubs()
linuxX64Stubs()
@@ -122,10 +141,3 @@
kotlinTarget = KotlinTarget.KOTLIN_1_9
}
-android {
- compileSdk 35
- namespace "androidx.compose.foundation.layout"
- buildTypes.configureEach {
- consumerProguardFiles("proguard-rules.pro")
- }
-}
diff --git a/compose/material/material-ripple/build.gradle b/compose/material/material-ripple/build.gradle
index b6d5e76..9385d8d 100644
--- a/compose/material/material-ripple/build.gradle
+++ b/compose/material/material-ripple/build.gradle
@@ -28,12 +28,24 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
id("AndroidXComposePlugin")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.compose.material.ripple"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+
+ compileSdk = 35
+ aarMetadata.minCompileSdk = 35
+ }
jvmStubs()
linuxX64Stubs()
@@ -116,7 +128,3 @@
kotlinTarget = KotlinTarget.KOTLIN_1_9
}
-android {
- compileSdk 35
- namespace "androidx.compose.material.ripple"
-}
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/TooltipBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/TooltipBenchmark.kt
index f33e079..31c5ed8 100644
--- a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/TooltipBenchmark.kt
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/TooltipBenchmark.kt
@@ -89,11 +89,11 @@
when (tooltipType) {
TooltipType.Plain -> {
tooltip = { PlainTooltipTest() }
- positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider()
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider()
}
TooltipType.Rich -> {
tooltip = { RichTooltipTest() }
- positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider()
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider()
}
}
diff --git a/compose/material3/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/material3/integration/macrobenchmark/target/TooltipActivity.kt b/compose/material3/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/material3/integration/macrobenchmark/target/TooltipActivity.kt
index bf222cbc..a2f6eb1 100644
--- a/compose/material3/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/material3/integration/macrobenchmark/target/TooltipActivity.kt
+++ b/compose/material3/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/material3/integration/macrobenchmark/target/TooltipActivity.kt
@@ -37,7 +37,7 @@
super.onCreate(savedInstanceState)
setContent {
TooltipBox(
- positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = {
PlainTooltip(caretSize = TooltipDefaults.caretSize) {
Text("Tooltip Description")
diff --git a/compose/material3/material3-adaptive-navigation-suite/build.gradle b/compose/material3/material3-adaptive-navigation-suite/build.gradle
index 8871853..65c3958 100644
--- a/compose/material3/material3-adaptive-navigation-suite/build.gradle
+++ b/compose/material3/material3-adaptive-navigation-suite/build.gradle
@@ -29,12 +29,24 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
id("AndroidXComposePlugin")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.compose.material3.adaptive.navigationsuite"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+
+ compileSdk = 35
+ aarMetadata.minCompileSdk = 35
+ }
jvmStubs()
defaultPlatform(PlatformIdentifier.ANDROID)
@@ -97,10 +109,6 @@
}
}
-android {
- compileSdk 35
- namespace "androidx.compose.material3.adaptive.navigationsuite"
-}
androidx {
name = "Material Adaptive Navigation Suite"
diff --git a/compose/material3/material3-common/build.gradle b/compose/material3/material3-common/build.gradle
index 0572958..3eac2d7 100644
--- a/compose/material3/material3-common/build.gradle
+++ b/compose/material3/material3-common/build.gradle
@@ -31,11 +31,23 @@
plugins {
id("AndroidXPlugin")
id("AndroidXComposePlugin")
- id("com.android.library")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.compose.material3.common"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+
+ compileSdk = 35
+ aarMetadata.minCompileSdk = 35
+ }
jvmStubs()
linuxX64Stubs()
@@ -99,10 +111,6 @@
}
}
-android {
- compileSdk 35
- namespace "androidx.compose.material3.common"
-}
androidx {
name = "Compose Material 3 Common"
diff --git a/compose/material3/material3-window-size-class/build.gradle b/compose/material3/material3-window-size-class/build.gradle
index a943601..46fb47e 100644
--- a/compose/material3/material3-window-size-class/build.gradle
+++ b/compose/material3/material3-window-size-class/build.gradle
@@ -28,12 +28,24 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
id("AndroidXComposePlugin")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.compose.material3.windowsizeclass"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+
+ compileSdk = 35
+ aarMetadata.minCompileSdk = 35
+ }
jvmStubs()
linuxX64Stubs()
@@ -114,7 +126,3 @@
kotlinTarget = KotlinTarget.KOTLIN_1_9
}
-android {
- compileSdk 35
- namespace "androidx.compose.material3.windowsizeclass"
-}
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index a43c6e0..28407dd 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -1929,7 +1929,8 @@
}
@SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class SheetState {
- ctor public SheetState(boolean skipPartiallyExpanded, androidx.compose.ui.unit.Density density, optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
+ ctor @Deprecated public SheetState(boolean skipPartiallyExpanded, androidx.compose.ui.unit.Density density, optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
+ ctor public SheetState(boolean skipPartiallyExpanded, kotlin.jvm.functions.Function0<java.lang.Float> positionalThreshold, kotlin.jvm.functions.Function0<java.lang.Float> velocityThreshold, optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
method public androidx.compose.material3.SheetValue getCurrentValue();
method public boolean getHasExpandedState();
@@ -1951,7 +1952,8 @@
}
public static final class SheetState.Companion {
- method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.SheetState,androidx.compose.material3.SheetValue> Saver(boolean skipPartiallyExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, androidx.compose.ui.unit.Density density, boolean skipHiddenState);
+ method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.SheetState,androidx.compose.material3.SheetValue> Saver(boolean skipPartiallyExpanded, kotlin.jvm.functions.Function0<java.lang.Float> positionalThreshold, kotlin.jvm.functions.Function0<java.lang.Float> velocityThreshold, kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, boolean skipHiddenState);
+ method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.SheetState,androidx.compose.material3.SheetValue> Saver(boolean skipPartiallyExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, androidx.compose.ui.unit.Density density, boolean skipHiddenState);
}
@SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public enum SheetValue {
@@ -2691,8 +2693,9 @@
method public float getPlainTooltipMaxWidth();
method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getRichTooltipContainerShape();
method public float getRichTooltipMaxWidth();
- method @androidx.compose.runtime.Composable public androidx.compose.ui.window.PopupPositionProvider rememberPlainTooltipPositionProvider(optional float spacingBetweenTooltipAndAnchor);
- method @androidx.compose.runtime.Composable public androidx.compose.ui.window.PopupPositionProvider rememberRichTooltipPositionProvider(optional float spacingBetweenTooltipAndAnchor);
+ method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.ui.window.PopupPositionProvider rememberPlainTooltipPositionProvider(optional float spacingBetweenTooltipAndAnchor);
+ method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.ui.window.PopupPositionProvider rememberRichTooltipPositionProvider(optional float spacingBetweenTooltipAndAnchor);
+ method @androidx.compose.runtime.Composable public androidx.compose.ui.window.PopupPositionProvider rememberTooltipPositionProvider(optional float spacingBetweenTooltipAndAnchor);
method @androidx.compose.runtime.Composable public androidx.compose.material3.RichTooltipColors richTooltipColors();
method @androidx.compose.runtime.Composable public androidx.compose.material3.RichTooltipColors richTooltipColors(optional long containerColor, optional long contentColor, optional long titleContentColor, optional long actionContentColor);
property public final long caretSize;
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index a43c6e0..28407dd 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -1929,7 +1929,8 @@
}
@SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class SheetState {
- ctor public SheetState(boolean skipPartiallyExpanded, androidx.compose.ui.unit.Density density, optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
+ ctor @Deprecated public SheetState(boolean skipPartiallyExpanded, androidx.compose.ui.unit.Density density, optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
+ ctor public SheetState(boolean skipPartiallyExpanded, kotlin.jvm.functions.Function0<java.lang.Float> positionalThreshold, kotlin.jvm.functions.Function0<java.lang.Float> velocityThreshold, optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
method public androidx.compose.material3.SheetValue getCurrentValue();
method public boolean getHasExpandedState();
@@ -1951,7 +1952,8 @@
}
public static final class SheetState.Companion {
- method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.SheetState,androidx.compose.material3.SheetValue> Saver(boolean skipPartiallyExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, androidx.compose.ui.unit.Density density, boolean skipHiddenState);
+ method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.SheetState,androidx.compose.material3.SheetValue> Saver(boolean skipPartiallyExpanded, kotlin.jvm.functions.Function0<java.lang.Float> positionalThreshold, kotlin.jvm.functions.Function0<java.lang.Float> velocityThreshold, kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, boolean skipHiddenState);
+ method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.SheetState,androidx.compose.material3.SheetValue> Saver(boolean skipPartiallyExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, androidx.compose.ui.unit.Density density, boolean skipHiddenState);
}
@SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public enum SheetValue {
@@ -2691,8 +2693,9 @@
method public float getPlainTooltipMaxWidth();
method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getRichTooltipContainerShape();
method public float getRichTooltipMaxWidth();
- method @androidx.compose.runtime.Composable public androidx.compose.ui.window.PopupPositionProvider rememberPlainTooltipPositionProvider(optional float spacingBetweenTooltipAndAnchor);
- method @androidx.compose.runtime.Composable public androidx.compose.ui.window.PopupPositionProvider rememberRichTooltipPositionProvider(optional float spacingBetweenTooltipAndAnchor);
+ method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.ui.window.PopupPositionProvider rememberPlainTooltipPositionProvider(optional float spacingBetweenTooltipAndAnchor);
+ method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.ui.window.PopupPositionProvider rememberRichTooltipPositionProvider(optional float spacingBetweenTooltipAndAnchor);
+ method @androidx.compose.runtime.Composable public androidx.compose.ui.window.PopupPositionProvider rememberTooltipPositionProvider(optional float spacingBetweenTooltipAndAnchor);
method @androidx.compose.runtime.Composable public androidx.compose.material3.RichTooltipColors richTooltipColors();
method @androidx.compose.runtime.Composable public androidx.compose.material3.RichTooltipColors richTooltipColors(optional long containerColor, optional long contentColor, optional long titleContentColor, optional long actionContentColor);
property public final long caretSize;
diff --git a/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/TooltipDemo.kt b/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/TooltipDemo.kt
index ec5eede..9e4c440 100644
--- a/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/TooltipDemo.kt
+++ b/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/TooltipDemo.kt
@@ -65,7 +65,7 @@
val textFieldTooltipState = rememberTooltipState()
val scope = rememberCoroutineScope()
TooltipBox(
- positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = { PlainTooltip { Text(textFieldTooltipText) } },
state = textFieldTooltipState
) {
@@ -96,7 +96,7 @@
LazyColumn(verticalArrangement = Arrangement.spacedBy(4.dp)) {
items(listData) { item ->
TooltipBox(
- positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = { PlainTooltip { Text("${item.itemName} added to list") } },
state = item.addedTooltipState
) {
@@ -115,7 +115,7 @@
headlineContent = { Text(itemName) },
trailingContent = {
TooltipBox(
- positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = { PlainTooltip { Text("Delete $itemName") } },
state = rememberTooltipState(),
enableUserInput = true
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TooltipSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TooltipSamples.kt
index bd37b75..1d8809e 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TooltipSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TooltipSamples.kt
@@ -50,7 +50,7 @@
@Composable
fun PlainTooltipSample() {
TooltipBox(
- positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = { PlainTooltip { Text("Add to favorites") } },
state = rememberTooltipState()
) {
@@ -69,7 +69,7 @@
val scope = rememberCoroutineScope()
Column(horizontalAlignment = Alignment.CenterHorizontally) {
TooltipBox(
- positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = { PlainTooltip { Text("Add to list") } },
state = tooltipState
) {
@@ -87,7 +87,7 @@
@Composable
fun PlainTooltipWithCaret() {
TooltipBox(
- positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = {
PlainTooltip(caretSize = TooltipDefaults.caretSize) { Text("Add to favorites") }
},
@@ -104,7 +104,7 @@
@Composable
fun PlainTooltipWithCustomCaret() {
TooltipBox(
- positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = { PlainTooltip(caretSize = DpSize(24.dp, 12.dp)) { Text("Add to favorites") } },
state = rememberTooltipState()
) {
@@ -121,7 +121,7 @@
val tooltipState = rememberTooltipState(isPersistent = true)
val scope = rememberCoroutineScope()
TooltipBox(
- positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = {
RichTooltip(
title = { Text(richTooltipSubheadText) },
@@ -150,7 +150,7 @@
val scope = rememberCoroutineScope()
Column(horizontalAlignment = Alignment.CenterHorizontally) {
TooltipBox(
- positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = {
RichTooltip(
title = { Text(richTooltipSubheadText) },
@@ -181,7 +181,7 @@
val tooltipState = rememberTooltipState(isPersistent = true)
val scope = rememberCoroutineScope()
TooltipBox(
- positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = {
RichTooltip(
title = { Text(richTooltipSubheadText) },
@@ -210,7 +210,7 @@
val tooltipState = rememberTooltipState(isPersistent = true)
val scope = rememberCoroutineScope()
TooltipBox(
- positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = {
RichTooltip(
title = { Text(richTooltipSubheadText) },
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt
index 40418e9..16a6375 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt
@@ -252,7 +252,12 @@
skipPartiallyExpanded = false,
skipHiddenState = true,
initialValue = SheetValue.PartiallyExpanded,
- density = rule.density
+ positionalThreshold = {
+ with(rule.density) { BottomSheetDefaults.PositionalThreshold.toPx() }
+ },
+ velocityThreshold = {
+ with(rule.density) { BottomSheetDefaults.VelocityThreshold.toPx() }
+ },
)
rule.setContent {
scope = rememberCoroutineScope()
@@ -923,7 +928,16 @@
)
var sheetCoords: LayoutCoordinates? = null
var rootCoords: LayoutCoordinates? = null
- val state = SheetState(false, density = Density(1f))
+ val state =
+ SheetState(
+ skipPartiallyExpanded = false,
+ positionalThreshold = {
+ with(rule.density) { BottomSheetDefaults.PositionalThreshold.toPx() }
+ },
+ velocityThreshold = {
+ with(rule.density) { BottomSheetDefaults.VelocityThreshold.toPx() }
+ },
+ )
var sheetValue by mutableStateOf(SheetValue.Hidden)
rule.setContent {
Box(Modifier.onGloballyPositioned { rootCoords = it }.offset { offset }) {
@@ -983,7 +997,16 @@
IntOffset(100, 10),
)
var sheetCoords: LayoutCoordinates? = null
- val state = SheetState(false, density = Density(1f))
+ val state =
+ SheetState(
+ skipPartiallyExpanded = false,
+ positionalThreshold = {
+ with(rule.density) { BottomSheetDefaults.PositionalThreshold.toPx() }
+ },
+ velocityThreshold = {
+ with(rule.density) { BottomSheetDefaults.VelocityThreshold.toPx() }
+ },
+ )
var sheetValue by mutableStateOf(SheetValue.Hidden)
rule.setContent {
LaunchedEffect(sheetValue) {
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
index 6453fe4..a6dc9a7 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
@@ -84,6 +84,7 @@
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.junit.Assume.assumeNotNull
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -187,7 +188,11 @@
rule.onNodeWithTag(EDMTag).assertIsDisplayed()
rule.onNodeWithTag(MenuItemTag).assertIsDisplayed()
- UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).pressBack()
+ val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ // First back closes keyboard
+ device.pressBack()
+ // Second back closes menu
+ device.pressBack()
rule.onNodeWithTag(TFTag).assertIsDisplayed()
rule.onNodeWithTag(MenuItemTag).assertDoesNotExist()
@@ -261,6 +266,7 @@
rule.onNodeWithTag(MenuItemTag).assertDoesNotExist()
}
+ @Ignore("b/374850853")
@Test
fun edm_editable_collapsesOnEscapePress() {
rule.setMaterialContent(lightColorScheme()) {
@@ -531,6 +537,46 @@
}
@Test
+ fun edm_anchorTypeIsUpdated_evenIfTextFieldIsNotClicked() {
+ var expanded by mutableStateOf(false)
+ var type: ExposedDropdownMenuAnchorType? = null
+ rule.setMaterialContent(lightColorScheme()) {
+ ExposedDropdownMenuBox(
+ expanded = expanded,
+ onExpandedChange = { expanded = it },
+ ) {
+ TextField(
+ modifier = Modifier.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable),
+ state = rememberTextFieldState(),
+ lineLimits = TextFieldLineLimits.SingleLine,
+ label = { Text("Label") },
+ trailingIcon = {
+ ExposedDropdownMenuDefaults.TrailingIcon(
+ expanded = expanded,
+ modifier =
+ Modifier.menuAnchor(
+ ExposedDropdownMenuAnchorType.SecondaryEditable
+ ),
+ )
+ }
+ )
+ ExposedDropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false },
+ ) {
+ DropdownMenuItem(
+ text = { Text(OptionName) },
+ onClick = {},
+ )
+ }
+ SideEffect { type = anchorType }
+ }
+ }
+ rule.runOnIdle { expanded = true }
+ assertThat(type).isEqualTo(ExposedDropdownMenuAnchorType.PrimaryEditable)
+ }
+
+ @Test
fun edm_widthMatchesTextFieldWidth() {
var textFieldBounds by mutableStateOf(Rect.Zero)
var menuBounds by mutableStateOf(Rect.Zero)
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
index 8fc3131..1293b79 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
@@ -125,9 +125,20 @@
@Test
fun modalBottomSheet_isDismissedOnTapOutside() {
var showBottomSheet by mutableStateOf(true)
- val sheetState = SheetState(skipPartiallyExpanded = false, density = rule.density)
+ lateinit var sheetState: SheetState
rule.setContent {
+ val density = LocalDensity.current
+ sheetState =
+ SheetState(
+ skipPartiallyExpanded = false,
+ positionalThreshold = {
+ with(density) { BottomSheetDefaults.PositionalThreshold.toPx() }
+ },
+ velocityThreshold = {
+ with(density) { BottomSheetDefaults.VelocityThreshold.toPx() }
+ },
+ )
if (showBottomSheet) {
ModalBottomSheet(
sheetState = sheetState,
@@ -160,8 +171,16 @@
@Test
fun modalBottomSheet_isDismissedOnSwipeDown() {
var showBottomSheet by mutableStateOf(true)
- val sheetState = SheetState(skipPartiallyExpanded = false, density = rule.density)
-
+ val sheetState =
+ SheetState(
+ skipPartiallyExpanded = false,
+ positionalThreshold = {
+ with(rule.density) { BottomSheetDefaults.PositionalThreshold.toPx() }
+ },
+ velocityThreshold = {
+ with(rule.density) { BottomSheetDefaults.VelocityThreshold.toPx() }
+ },
+ )
rule.setContent {
if (showBottomSheet) {
ModalBottomSheet(
@@ -362,9 +381,20 @@
@Test
fun modalBottomSheet_shortSheet_isDismissedOnBackPress() {
var showBottomSheet by mutableStateOf(true)
- val sheetState = SheetState(skipPartiallyExpanded = true, density = rule.density)
+ lateinit var sheetState: SheetState
rule.setContent {
+ val density = LocalDensity.current
+ sheetState =
+ SheetState(
+ skipPartiallyExpanded = true,
+ positionalThreshold = {
+ with(density) { BottomSheetDefaults.PositionalThreshold.toPx() }
+ },
+ velocityThreshold = {
+ with(density) { BottomSheetDefaults.VelocityThreshold.toPx() }
+ },
+ )
val dispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
if (showBottomSheet) {
ModalBottomSheet(
@@ -395,9 +425,20 @@
@Test
fun modalBottomSheet_tallSheet_isDismissedOnBackPress() {
var showBottomSheet by mutableStateOf(true)
- val sheetState = SheetState(skipPartiallyExpanded = false, density = rule.density)
+ lateinit var sheetState: SheetState
rule.setContent {
+ val density = LocalDensity.current
+ sheetState =
+ SheetState(
+ skipPartiallyExpanded = false,
+ positionalThreshold = {
+ with(density) { BottomSheetDefaults.PositionalThreshold.toPx() }
+ },
+ velocityThreshold = {
+ with(density) { BottomSheetDefaults.VelocityThreshold.toPx() }
+ },
+ )
val dispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
if (showBottomSheet) {
ModalBottomSheet(
@@ -611,10 +652,21 @@
fun modalBottomSheet_missingAnchors_findsClosest() {
val topTag = "ModalBottomSheetLayout"
var showShortContent by mutableStateOf(false)
- val sheetState = SheetState(skipPartiallyExpanded = false, density = rule.density)
+ lateinit var sheetState: SheetState
lateinit var scope: CoroutineScope
rule.setContent {
+ val density = LocalDensity.current
+ sheetState =
+ SheetState(
+ skipPartiallyExpanded = false,
+ positionalThreshold = {
+ with(density) { BottomSheetDefaults.PositionalThreshold.toPx() }
+ },
+ velocityThreshold = {
+ with(density) { BottomSheetDefaults.VelocityThreshold.toPx() }
+ },
+ )
scope = rememberCoroutineScope()
ModalBottomSheet(
onDismissRequest = {},
@@ -790,8 +842,20 @@
@Test
fun modalBottomSheet_testParialExpandReturnsIllegalStateException_whenSkipPartialExpanded() {
lateinit var scope: CoroutineScope
- val bottomSheetState = SheetState(skipPartiallyExpanded = true, density = rule.density)
+ lateinit var bottomSheetState: SheetState
+
rule.setContent {
+ val density = LocalDensity.current
+ bottomSheetState =
+ SheetState(
+ skipPartiallyExpanded = true,
+ positionalThreshold = {
+ with(density) { BottomSheetDefaults.PositionalThreshold.toPx() }
+ },
+ velocityThreshold = {
+ with(density) { BottomSheetDefaults.VelocityThreshold.toPx() }
+ },
+ )
scope = rememberCoroutineScope()
ModalBottomSheet(
onDismissRequest = {},
@@ -931,10 +995,22 @@
@Test
fun modalBottomSheet_shortSheet_anchorChangeHandler_previousTargetNotInAnchors_reconciles() {
- val sheetState = SheetState(skipPartiallyExpanded = false, density = rule.density)
var hasSheetContent by mutableStateOf(false) // Start out with empty sheet content
lateinit var scope: CoroutineScope
+ lateinit var sheetState: SheetState
+
rule.setContent {
+ val density = LocalDensity.current
+ sheetState =
+ SheetState(
+ skipPartiallyExpanded = false,
+ positionalThreshold = {
+ with(density) { BottomSheetDefaults.PositionalThreshold.toPx() }
+ },
+ velocityThreshold = {
+ with(density) { BottomSheetDefaults.VelocityThreshold.toPx() }
+ },
+ )
scope = rememberCoroutineScope()
ModalBottomSheet(
@@ -968,10 +1044,22 @@
@Test
fun modalBottomSheet_tallSheet_anchorChangeHandler_previousTargetNotInAnchors_reconciles() {
- val sheetState = SheetState(skipPartiallyExpanded = false, density = rule.density)
var hasSheetContent by mutableStateOf(false) // Start out with empty sheet content
lateinit var scope: CoroutineScope
+ lateinit var sheetState: SheetState
+
rule.setContent {
+ val density = LocalDensity.current
+ sheetState =
+ SheetState(
+ skipPartiallyExpanded = false,
+ positionalThreshold = {
+ with(density) { BottomSheetDefaults.PositionalThreshold.toPx() }
+ },
+ velocityThreshold = {
+ with(density) { BottomSheetDefaults.VelocityThreshold.toPx() }
+ },
+ )
scope = rememberCoroutineScope()
ModalBottomSheet(
onDismissRequest = {},
@@ -1006,8 +1094,6 @@
fun modalBottomSheet_callsOnDismissRequest_onNestedScrollFling() {
var callCount by mutableStateOf(0)
val expectedCallCount = 1
- val sheetState = SheetState(skipPartiallyExpanded = true, density = rule.density)
-
val nestedScrollDispatcher = NestedScrollDispatcher()
val nestedScrollConnection =
object : NestedScrollConnection {
@@ -1015,7 +1101,20 @@
}
lateinit var scope: CoroutineScope
+ lateinit var sheetState: SheetState
+
rule.setContent {
+ val density = LocalDensity.current
+ sheetState =
+ SheetState(
+ skipPartiallyExpanded = true,
+ positionalThreshold = {
+ with(density) { BottomSheetDefaults.PositionalThreshold.toPx() }
+ },
+ velocityThreshold = {
+ with(density) { BottomSheetDefaults.VelocityThreshold.toPx() }
+ },
+ )
scope = rememberCoroutineScope()
ModalBottomSheet(
onDismissRequest = { callCount += 1 },
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt
index feb118a..f2b416d 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt
@@ -120,7 +120,7 @@
private fun PlainTooltipTest() {
val tooltipState = rememberTooltipState()
TooltipBox(
- positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = {
PlainTooltip(modifier = Modifier.testTag(TooltipTestTag)) {
Text("Tooltip Description")
@@ -137,7 +137,7 @@
private fun RichTooltipTest() {
val tooltipState = rememberTooltipState(isPersistent = true)
TooltipBox(
- positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = {
RichTooltip(
title = { Text("Title") },
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipTest.kt
index 0cfc573..fccc9c2 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipTest.kt
@@ -465,9 +465,7 @@
// Plain tooltip positioning
lateinit var positionProvider: PopupPositionProvider
- rule.setContent {
- positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider()
- }
+ rule.setContent { positionProvider = TooltipDefaults.rememberTooltipPositionProvider() }
val tooltipPosition =
positionProvider.calculatePosition(
@@ -500,7 +498,7 @@
// Rich tooltip positioning
lateinit var positionProvider: PopupPositionProvider
- rule.setContent { positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider() }
+ rule.setContent { positionProvider = TooltipDefaults.rememberTooltipPositionProvider() }
val tooltipPosition =
positionProvider.calculatePosition(
@@ -521,7 +519,7 @@
var anchorBounds = Rect.Zero
rule.setContent {
TooltipBox(
- positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
state = rememberTooltipState(initialIsVisible = true, isPersistent = true),
tooltip = {
PlainTooltip(
@@ -565,7 +563,7 @@
var anchorBounds = Rect.Zero
rule.setContent {
TooltipBox(
- positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
state = rememberTooltipState(initialIsVisible = true, isPersistent = true),
tooltip = {
RichTooltip(
@@ -615,7 +613,7 @@
topState = rememberTooltipState(isPersistent = true)
bottomState = rememberTooltipState(isPersistent = true)
TooltipBox(
- positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = {
RichTooltip(
title = {
@@ -636,7 +634,7 @@
scope.launch { topState.show() }
TooltipBox(
- positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = {
RichTooltip(
title = {
@@ -679,7 +677,7 @@
val scope = rememberCoroutineScope()
topState = rememberTooltipState(isPersistent = true, mutatorMutex = MutatorMutex())
TooltipBox(
- positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = {
RichTooltip(
title = {
@@ -701,7 +699,7 @@
bottomState = rememberTooltipState(isPersistent = true, mutatorMutex = MutatorMutex())
TooltipBox(
- positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = {
RichTooltip(
title = {
@@ -741,7 +739,7 @@
tooltipState: TooltipState = rememberTooltipState(),
) {
TooltipBox(
- positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = {
PlainTooltip(
modifier = modifier.testTag(ContainerTestTag),
@@ -763,7 +761,7 @@
tooltipState: TooltipState = rememberTooltipState(action != null),
) {
TooltipBox(
- positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = {
RichTooltip(
title = title,
@@ -782,7 +780,7 @@
fun plainTooltip_withClickable_hasCorrectSemantics() {
rule.setMaterialContent(lightColorScheme()) {
TooltipBox(
- positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
+ positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = {
PlainTooltip(
modifier = Modifier.testTag(ContainerTestTag),
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/WavyProgressIndicatorTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/WavyProgressIndicatorTest.kt
index b4fa43b..edfc828 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/WavyProgressIndicatorTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/WavyProgressIndicatorTest.kt
@@ -161,21 +161,29 @@
fun determinateLinearWavyProgressIndicator_sizeModifier() {
val expectedWidth = 100.dp
val expectedHeight = 10.dp
- val expectedSize =
- with(rule.density) { IntSize(expectedWidth.roundToPx(), expectedHeight.roundToPx()) }
val tag = "linear"
- var trackColor = Color.Unspecified
- var progressColor = Color.Unspecified
- rule.setContent {
- trackColor = ProgressIndicatorDefaults.linearTrackColor
- progressColor = ProgressIndicatorDefaults.linearColor
-
- Box(Modifier.testTag(tag)) {
+ val contentToTest =
+ rule.setMaterialContentForSizeAssertions {
LinearWavyProgressIndicator(
- modifier = Modifier.size(expectedWidth, expectedHeight),
+ modifier = Modifier.size(expectedWidth, expectedHeight).testTag(tag),
progress = { 0.5f }
)
}
+
+ contentToTest.assertWidthIsEqualTo(expectedWidth).assertHeightIsEqualTo(expectedHeight)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun determinateLinearWavyProgressIndicator_colors() {
+ val tag = "linear"
+ var trackColor = Color.Unspecified
+ var progressColor = Color.Unspecified
+ rule.setMaterialContentForSizeAssertions {
+ trackColor = ProgressIndicatorDefaults.linearTrackColor
+ progressColor = ProgressIndicatorDefaults.linearColor
+
+ Box(Modifier.testTag(tag)) { LinearWavyProgressIndicator(progress = { 0.5f }) }
}
rule
@@ -183,11 +191,6 @@
.captureToImage()
.assertContainsColor(trackColor)
.assertContainsColor(progressColor)
- .toPixelMap()
- .let {
- assertEquals(expectedSize.width, it.width)
- assertEquals(expectedSize.height, it.height)
- }
}
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
@@ -195,24 +198,29 @@
fun indeterminateLinearWavyProgressIndicator_sizeModifier() {
val expectedWidth = 100.dp
val expectedHeight = 10.dp
- val expectedSize =
- with(rule.density) { IntSize(expectedWidth.roundToPx(), expectedHeight.roundToPx()) }
- rule.mainClock.autoAdvance = false
val tag = "linear"
- rule.setContent {
- Box(Modifier.testTag(tag)) {
+ val contentToTest =
+ rule.setMaterialContentForSizeAssertions {
LinearWavyProgressIndicator(
- modifier = Modifier.size(expectedWidth, expectedHeight),
- color = Color.Blue
+ modifier = Modifier.size(expectedWidth, expectedHeight).testTag(tag),
)
}
+
+ contentToTest.assertWidthIsEqualTo(expectedWidth).assertHeightIsEqualTo(expectedHeight)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun indeterminateLinearWavyProgressIndicator_colors() {
+ rule.mainClock.autoAdvance = false
+ val tag = "linear"
+ rule.setMaterialContentForSizeAssertions {
+ Box(Modifier.testTag(tag)) { LinearWavyProgressIndicator(color = Color.Blue) }
}
rule.mainClock.advanceTimeBy(300)
rule.onNodeWithTag(tag).captureToImage().toPixelMap().let {
- assertEquals(expectedSize.width, it.width)
- assertEquals(expectedSize.height, it.height)
// Assert that a center pixel relatively at the start of the path has the right
// progress color.
it.assertPixelColor(Color.Blue, 5, it.height / 2)
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.android.kt
index 9c9a97b..d67fed8 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.android.kt
@@ -68,6 +68,8 @@
import androidx.compose.ui.layout.layout
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInWindow
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
@@ -168,6 +170,13 @@
): Modifier =
this.focusRequester(focusRequester)
.then(
+ ExposedDropdownMenuAnchorElement {
+ if (type.hasGreaterOrEqualPriorityThan(anchorTypeState.value)) {
+ anchorTypeState.value = type
+ }
+ }
+ )
+ .then(
if (!enabled) Modifier
else
Modifier.expandable(
@@ -516,6 +525,17 @@
override fun toString(): String = name
}
+private fun ExposedDropdownMenuAnchorType.hasGreaterOrEqualPriorityThan(
+ that: ExposedDropdownMenuAnchorType
+): Boolean =
+ when (this) {
+ ExposedDropdownMenuAnchorType.PrimaryNotEditable,
+ ExposedDropdownMenuAnchorType.PrimaryEditable -> true
+ ExposedDropdownMenuAnchorType.SecondaryEditable ->
+ that == ExposedDropdownMenuAnchorType.SecondaryEditable
+ else -> false
+ }
+
@Deprecated(
message = "Renamed to ExposedDropdownMenuAnchorType",
replaceWith = ReplaceWith("ExposedDropdownMenuAnchorType"),
@@ -1434,6 +1454,29 @@
}
}
+private data class ExposedDropdownMenuAnchorElement(
+ val updateStateOnAttach: () -> Unit,
+) : ModifierNodeElement<ExposedDropdownMenuAnchorNode>() {
+ override fun create() = ExposedDropdownMenuAnchorNode(updateStateOnAttach)
+
+ override fun update(node: ExposedDropdownMenuAnchorNode) {
+ node.updateStateOnAttach = updateStateOnAttach
+ }
+
+ override fun InspectorInfo.inspectableProperties() {
+ name = "exposedDropdownMenuAnchorType"
+ properties["updateStateOnAttach"] = updateStateOnAttach
+ }
+}
+
+private class ExposedDropdownMenuAnchorNode(
+ var updateStateOnAttach: () -> Unit,
+) : Modifier.Node() {
+ override fun onAttach() {
+ updateStateOnAttach()
+ }
+}
+
private fun Modifier.expandable(
expanded: Boolean,
onExpandedChange: () -> Unit,
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/Tooltip.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/Tooltip.android.kt
index c2c9412..ffa265c 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/Tooltip.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/Tooltip.android.kt
@@ -125,7 +125,6 @@
val configuration = LocalConfiguration.current
Modifier.drawCaret { anchorLayoutCoordinates ->
drawCaretWithPath(
- CaretType.Plain,
density,
configuration,
containerColor,
@@ -253,7 +252,6 @@
val configuration = LocalConfiguration.current
Modifier.drawCaret { anchorLayoutCoordinates ->
drawCaretWithPath(
- CaretType.Rich,
density,
configuration,
elevatedColor,
@@ -315,7 +313,6 @@
@ExperimentalMaterial3Api
private fun CacheDrawScope.drawCaretWithPath(
- caretType: CaretType,
density: Density,
configuration: Configuration,
containerColor: Color,
@@ -351,42 +348,27 @@
tooltipHeight
}
- val position: Offset
- if (caretType == CaretType.Plain) {
- position =
- if (anchorMid + tooltipWidth / 2 > screenWidthPx) {
- // Caret needs to be near the right
- val anchorMidFromRightScreenEdge = screenWidthPx - anchorMid
- val caretX = tooltipWidth - anchorMidFromRightScreenEdge
- Offset(caretX, caretY)
- } else {
- // Caret needs to be near the left
- val tooltipLeft = anchorLeft - (this.size.width / 2 - anchorWidth / 2)
- val caretX = anchorMid - maxOf(tooltipLeft, 0f)
- Offset(caretX, caretY)
- }
- } else {
- // Default the caret to the left
- var preferredPosition = Offset(anchorMid - anchorLeft, caretY)
- if (anchorLeft + tooltipWidth > screenWidthPx) {
- // Need to move the caret to the right
- preferredPosition = Offset(anchorMid - (anchorRight - tooltipWidth), caretY)
- if (anchorRight - tooltipWidth < 0) {
- // Need to center the caret
- // Caret might need to be offset depending on where
- // the tooltip is placed relative to the anchor
- if (anchorLeft - tooltipWidth / 2 + anchorWidth / 2 <= 0) {
- preferredPosition = Offset(anchorMid, caretY)
- } else if (anchorRight + tooltipWidth / 2 - anchorWidth / 2 >= screenWidthPx) {
- val anchorMidFromRightScreenEdge = screenWidthPx - anchorMid
- val caretX = tooltipWidth - anchorMidFromRightScreenEdge
- preferredPosition = Offset(caretX, caretY)
- } else {
- preferredPosition = Offset(tooltipWidth / 2, caretY)
- }
- }
+ // Default the caret to be in the middle
+ // caret might need to be offset depending on where
+ // the tooltip is placed relative to the anchor
+ var position: Offset =
+ if (anchorLeft - tooltipWidth / 2 + anchorWidth / 2 <= 0) {
+ Offset(anchorMid, caretY)
+ } else if (anchorRight + tooltipWidth / 2 - anchorWidth / 2 >= screenWidthPx) {
+ val anchorMidFromRightScreenEdge = screenWidthPx - anchorMid
+ val caretX = tooltipWidth - anchorMidFromRightScreenEdge
+ Offset(caretX, caretY)
+ } else {
+ Offset(tooltipWidth / 2, caretY)
}
- position = preferredPosition
+ if (anchorMid - tooltipWidth / 2 < 0) {
+ // The tooltip needs to be start aligned if it would collide with the left side of
+ // screen.
+ position = Offset(anchorMid - anchorLeft, caretY)
+ } else if (anchorMid + tooltipWidth / 2 > screenWidthPx) {
+ // The tooltip needs to be end aligned if it would collide with the right side of the
+ // screen.
+ position = Offset(anchorMid - (anchorRight - tooltipWidth), caretY)
}
if (isCaretTop) {
@@ -415,9 +397,3 @@
}
}
}
-
-@ExperimentalMaterial3Api
-private enum class CaretType {
- Plain,
- Rich
-}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DragHandle.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DragHandle.kt
new file mode 100644
index 0000000..da5127e
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DragHandle.kt
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2024 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.material3
+
+import androidx.compose.foundation.gestures.PressGestureScope
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.hoverable
+import androidx.compose.foundation.indication
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsDraggedAsState
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.DragHandleDefaults.dragHandleColors
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.graphics.isSpecified
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastRoundToInt
+
+@Composable
+internal fun VerticalDragHandle(
+ modifier: Modifier = Modifier,
+ sizes: DragHandleSizes = DragHandleDefaults.DefaultDragHandleSizes,
+ colors: DragHandleColors = MaterialTheme.colorScheme.dragHandleColors(),
+ shapes: DragHandleShapes = DragHandleDefaults.DefaultDragHandleShapes,
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+) {
+ val isDragged by interactionSource.collectIsDraggedAsState()
+ var isPressed by remember { mutableStateOf(false) }
+ Box(
+ modifier =
+ modifier
+ .minimumInteractiveComponentSize()
+ .hoverable(interactionSource)
+ .pressable(interactionSource) { _ ->
+ isPressed = true
+ tryAwaitRelease()
+ isPressed = false
+ }
+ .graphicsLayer {
+ shape = if (isDragged || isPressed) shapes.pressedShape else shapes.defaultShape
+ clip = true
+ }
+ .layout { measurable, _ ->
+ val dragHandleSize =
+ if (isDragged || isPressed) {
+ sizes.pressedSize
+ } else {
+ sizes.defaultSize
+ }
+ .toSize()
+ // set constraints here to be the size needed
+ val placeable =
+ measurable.measure(
+ Constraints.fixed(
+ dragHandleSize.width.fastRoundToInt(),
+ dragHandleSize.height.fastRoundToInt()
+ )
+ )
+ layout(placeable.width, placeable.height) { placeable.placeRelative(0, 0) }
+ }
+ .drawBehind {
+ drawRect(
+ if (isDragged || isPressed) colors.pressedColor else colors.defaultColor
+ )
+ }
+ .indication(interactionSource, ripple())
+ )
+}
+
+@Immutable
+internal class DragHandleColors(val defaultColor: Color, val pressedColor: Color) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || other !is DragHandleColors) return false
+ if (defaultColor != other.defaultColor) return false
+ if (pressedColor != other.pressedColor) return false
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = defaultColor.hashCode()
+ result = 31 * result + pressedColor.hashCode()
+ return result
+ }
+}
+
+@Immutable
+internal class DragHandleShapes(val defaultShape: Shape, val pressedShape: Shape) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || other !is DragHandleShapes) return false
+ if (defaultShape != other.defaultShape) return false
+ if (pressedShape != other.pressedShape) return false
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = defaultShape.hashCode()
+ result = 31 * result + pressedShape.hashCode()
+ return result
+ }
+}
+
+@Immutable
+internal class DragHandleSizes(val defaultSize: DpSize, val pressedSize: DpSize) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || other !is DragHandleSizes) return false
+ if (defaultSize != other.defaultSize) return false
+ if (pressedSize != other.pressedSize) return false
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = defaultSize.hashCode()
+ result = 31 * result + pressedSize.hashCode()
+ return result
+ }
+}
+
+// TODO(b/343194663): Introduce tokens and theming
+internal object DragHandleDefaults {
+ @Composable
+ fun dragHandleColors(
+ defaultColor: Color = Color.Unspecified,
+ pressedColor: Color = Color.Unspecified
+ ): DragHandleColors = MaterialTheme.colorScheme.dragHandleColors(defaultColor, pressedColor)
+
+ fun dragHandleShapes(
+ defaultShape: Shape = CircleShape,
+ pressedShape: Shape = RoundedCornerShape(12.dp)
+ ): DragHandleShapes = DragHandleShapes(defaultShape, pressedShape)
+
+ fun dragHandleSizes(
+ defaultSize: DpSize = DpSize(4.dp, 48.dp),
+ pressedSize: DpSize = DpSize(12.dp, 52.dp)
+ ): DragHandleSizes = DragHandleSizes(defaultSize, pressedSize)
+
+ internal fun ColorScheme.dragHandleColors(
+ defaultColor: Color = Color.Unspecified,
+ pressedColor: Color = Color.Unspecified
+ ): DragHandleColors =
+ DragHandleColors(
+ if (defaultColor.isSpecified) defaultColor else outline,
+ if (pressedColor.isSpecified) pressedColor else onSurface
+ )
+
+ internal val DefaultDragHandleShapes = dragHandleShapes()
+
+ internal val DefaultDragHandleSizes = dragHandleSizes()
+}
+
+private fun Modifier.pressable(
+ interactionSource: MutableInteractionSource,
+ onPress: suspend PressGestureScope.(Offset) -> Unit,
+): Modifier = pointerInput(interactionSource) { detectTapGestures(onPress = onPress) }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Label.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Label.kt
index 27ac94b..b3a8bee 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Label.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Label.kt
@@ -41,7 +41,7 @@
/**
* Label component that will append a [label] to [content]. The positioning logic uses
- * [TooltipDefaults.rememberPlainTooltipPositionProvider].
+ * [TooltipDefaults.rememberTooltipPositionProvider].
*
* Label appended to thumbs of Slider:
*
@@ -71,7 +71,7 @@
@Suppress("NAME_SHADOWING")
val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
// Has the same positioning logic as PlainTooltips
- val positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider()
+ val positionProvider = TooltipDefaults.rememberTooltipPositionProvider()
val state =
if (isPersistent) remember { LabelStateImpl() }
else rememberBasicTooltipState(mutatorMutex = MutatorMutex())
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt
index 12620b9..66d3d8d 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt
@@ -69,8 +69,15 @@
* should be skipped. If true, the sheet will always expand to the [Expanded] state and move to
* the [Hidden] state if available when hiding the sheet, either programmatically or by user
* interaction.
+ * @param positionalThreshold The positional threshold, in px, to be used when calculating the
+ * target state while a drag is in progress and when settling after the drag ends. This is the
+ * distance from the start of a transition. It will be, depending on the direction of the
+ * interaction, added or subtracted from/to the origin offset. It should always be a positive
+ * value.
+ * @param velocityThreshold The velocity threshold (in px per second) that the end velocity has to
+ * exceed in order to animate to the next state, even if the [positionalThreshold] has not been
+ * reached.
* @param initialValue The initial value of the state.
- * @param density The density that this state can use to convert values to and from dp.
* @param confirmValueChange Optional callback invoked to confirm or veto a pending state change.
* @param skipHiddenState Whether the hidden state should be skipped. If true, the sheet will always
* expand to the [Expanded] state and move to the [PartiallyExpanded] if available, either
@@ -80,11 +87,13 @@
@ExperimentalMaterial3Api
class SheetState(
internal val skipPartiallyExpanded: Boolean,
- density: Density,
+ positionalThreshold: () -> Float,
+ velocityThreshold: () -> Float,
initialValue: SheetValue = Hidden,
confirmValueChange: (SheetValue) -> Boolean = { true },
internal val skipHiddenState: Boolean = false,
) {
+
init {
if (skipPartiallyExpanded) {
require(initialValue != PartiallyExpanded) {
@@ -270,8 +279,8 @@
initialValue = initialValue,
animationSpec = { anchoredDraggableMotionSpec },
confirmValueChange = confirmValueChange,
- positionalThreshold = { with(density) { 56.dp.toPx() } },
- velocityThreshold = { with(density) { 125.dp.toPx() } },
+ positionalThreshold = { positionalThreshold() },
+ velocityThreshold = velocityThreshold,
)
internal val offset: Float
@@ -285,8 +294,9 @@
/** The default [Saver] implementation for [SheetState]. */
fun Saver(
skipPartiallyExpanded: Boolean,
+ positionalThreshold: () -> Float,
+ velocityThreshold: () -> Float,
confirmValueChange: (SheetValue) -> Boolean,
- density: Density,
skipHiddenState: Boolean,
) =
Saver<SheetState, SheetValue>(
@@ -294,14 +304,53 @@
restore = { savedValue ->
SheetState(
skipPartiallyExpanded,
- density,
+ positionalThreshold,
+ velocityThreshold,
savedValue,
confirmValueChange,
skipHiddenState,
)
}
)
+
+ @Deprecated(
+ level = DeprecationLevel.HIDDEN,
+ message = "Maintained for binary compatibility."
+ )
+ fun Saver(
+ skipPartiallyExpanded: Boolean,
+ confirmValueChange: (SheetValue) -> Boolean,
+ density: Density,
+ skipHiddenState: Boolean,
+ ) =
+ Saver(
+ skipPartiallyExpanded = skipPartiallyExpanded,
+ confirmValueChange = confirmValueChange,
+ skipHiddenState = skipHiddenState,
+ positionalThreshold = {
+ with(density) { BottomSheetDefaults.PositionalThreshold.toPx() }
+ },
+ velocityThreshold = {
+ with(density) { BottomSheetDefaults.VelocityThreshold.toPx() }
+ }
+ )
}
+
+ @Deprecated(level = DeprecationLevel.HIDDEN, message = "Maintained for binary compatibility.")
+ constructor(
+ skipPartiallyExpanded: Boolean,
+ density: Density,
+ initialValue: SheetValue = Hidden,
+ confirmValueChange: (SheetValue) -> Boolean = { true },
+ skipHiddenState: Boolean = false,
+ ) : this(
+ skipPartiallyExpanded = skipPartiallyExpanded,
+ positionalThreshold = { with(density) { BottomSheetDefaults.PositionalThreshold.toPx() } },
+ velocityThreshold = { with(density) { BottomSheetDefaults.VelocityThreshold.toPx() } },
+ initialValue = initialValue,
+ confirmValueChange = confirmValueChange,
+ skipHiddenState = skipHiddenState,
+ )
}
/** Possible values of [SheetState]. */
@@ -350,6 +399,10 @@
val windowInsets: WindowInsets
@Composable get() = WindowInsets.safeDrawing.only(WindowInsetsSides.Bottom)
+ internal val PositionalThreshold = 56.dp
+
+ internal val VelocityThreshold = 125.dp
+
/** The optional visual marker placed on top of a bottom sheet to indicate it may be dragged. */
@Composable
fun DragHandle(
@@ -439,8 +492,12 @@
confirmValueChange: (SheetValue) -> Boolean = { true },
initialValue: SheetValue = Hidden,
skipHiddenState: Boolean = false,
+ positionalThreshold: Dp = BottomSheetDefaults.PositionalThreshold,
+ velocityThreshold: Dp = BottomSheetDefaults.VelocityThreshold,
): SheetState {
val density = LocalDensity.current
+ val positionalThresholdToPx = { with(density) { positionalThreshold.toPx() } }
+ val velocityThresholdToPx = { with(density) { velocityThreshold.toPx() } }
return rememberSaveable(
skipPartiallyExpanded,
confirmValueChange,
@@ -448,14 +505,16 @@
saver =
SheetState.Saver(
skipPartiallyExpanded = skipPartiallyExpanded,
+ positionalThreshold = positionalThresholdToPx,
+ velocityThreshold = velocityThresholdToPx,
confirmValueChange = confirmValueChange,
- density = density,
skipHiddenState = skipHiddenState,
)
) {
SheetState(
skipPartiallyExpanded,
- density,
+ positionalThresholdToPx,
+ velocityThresholdToPx,
initialValue,
confirmValueChange,
skipHiddenState,
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt
index c345413..0784a16 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt
@@ -374,6 +374,12 @@
*
* @param spacingBetweenTooltipAndAnchor the spacing between the tooltip and the anchor content.
*/
+ @Deprecated(
+ "Deprecated in favor of rememberTooltipPositionProvider API.",
+ replaceWith =
+ ReplaceWith("rememberTooltipPositionProvider(spacingBetweenTooltipAndAnchor)"),
+ level = DeprecationLevel.HIDDEN
+ )
@Composable
fun rememberPlainTooltipPositionProvider(
spacingBetweenTooltipAndAnchor: Dp = SpacingBetweenTooltipAndAnchor
@@ -407,6 +413,12 @@
*
* @param spacingBetweenTooltipAndAnchor the spacing between the tooltip and the anchor content.
*/
+ @Deprecated(
+ "Deprecated in favor of rememberTooltipPositionProvider API.",
+ replaceWith =
+ ReplaceWith("rememberTooltipPositionProvider(spacingBetweenTooltipAndAnchor)"),
+ level = DeprecationLevel.HIDDEN
+ )
@Composable
fun rememberRichTooltipPositionProvider(
spacingBetweenTooltipAndAnchor: Dp = SpacingBetweenTooltipAndAnchor
@@ -443,6 +455,53 @@
}
}
}
+
+ /**
+ * [PopupPositionProvider] that should be used with either [RichTooltip] or [PlainTooltip]. It
+ * correctly positions the tooltip in respect to the anchor content.
+ *
+ * @param spacingBetweenTooltipAndAnchor the spacing between the tooltip and the anchor content.
+ */
+ @Composable
+ fun rememberTooltipPositionProvider(
+ spacingBetweenTooltipAndAnchor: Dp = SpacingBetweenTooltipAndAnchor
+ ): PopupPositionProvider {
+ val tooltipAnchorSpacing =
+ with(LocalDensity.current) { spacingBetweenTooltipAndAnchor.roundToPx() }
+ return remember(tooltipAnchorSpacing) {
+ object : PopupPositionProvider {
+ override fun calculatePosition(
+ anchorBounds: IntRect,
+ windowSize: IntSize,
+ layoutDirection: LayoutDirection,
+ popupContentSize: IntSize
+ ): IntOffset {
+ // Horizontal alignment preference: middle -> start -> end
+ // Vertical preference: above -> below
+
+ // Tooltip prefers to be center aligned horizontally.
+ var x = anchorBounds.left + (anchorBounds.width - popupContentSize.width) / 2
+
+ if (x < 0) {
+ // Make tooltip start aligned if colliding with the
+ // left side of the screen
+ x = anchorBounds.left
+ } else if (x + popupContentSize.width > windowSize.width) {
+ // Make tooltip end aligned if colliding with the
+ // right side of the screen
+ x = anchorBounds.right - popupContentSize.width
+ }
+
+ // Tooltip prefers to be above the anchor,
+ // but if this causes the tooltip to overlap with the anchor
+ // then we place it below the anchor
+ var y = anchorBounds.top - popupContentSize.height - tooltipAnchorSpacing
+ if (y < 0) y = anchorBounds.bottom + tooltipAnchorSpacing
+ return IntOffset(x, y)
+ }
+ }
+ }
+ }
}
@Stable
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/WavyProgressIndicator.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/WavyProgressIndicator.kt
index 70fea23..3debdfc 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/WavyProgressIndicator.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/WavyProgressIndicator.kt
@@ -30,7 +30,6 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.requiredSizeIn
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.progressSemantics
import androidx.compose.material3.internal.IncreaseVerticalSemanticsBounds
@@ -185,7 +184,6 @@
.semantics(mergeDescendants = true) {
progressBarRangeInfo = ProgressBarRangeInfo(coercedProgress(), 0f..1f)
}
- .requiredSizeIn(minWidth = LinearContainerMinWidth)
.size(
width = WavyProgressIndicatorDefaults.LinearContainerWidth,
height = WavyProgressIndicatorDefaults.LinearContainerHeight
@@ -373,7 +371,6 @@
modifier
.then(IncreaseVerticalSemanticsBounds)
.progressSemantics()
- .requiredSizeIn(minWidth = LinearContainerMinWidth)
.size(
WavyProgressIndicatorDefaults.LinearContainerWidth,
WavyProgressIndicatorDefaults.LinearContainerHeight
diff --git a/compose/runtime/runtime-test-utils/build.gradle b/compose/runtime/runtime-test-utils/build.gradle
index f5ee280..f9f82e1 100644
--- a/compose/runtime/runtime-test-utils/build.gradle
+++ b/compose/runtime/runtime-test-utils/build.gradle
@@ -19,12 +19,21 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
id("AndroidXComposePlugin")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.compose.runtime.testutils"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ }
jvmStubs()
linuxX64Stubs()
@@ -73,6 +82,3 @@
description = "Compose runtime test utils shared between runtime and compiler tests."
}
-android {
- namespace "androidx.compose.runtime.testutils"
-}
diff --git a/compose/runtime/runtime/integration-tests/build.gradle b/compose/runtime/runtime/integration-tests/build.gradle
index 233a8eb..791773c 100644
--- a/compose/runtime/runtime/integration-tests/build.gradle
+++ b/compose/runtime/runtime/integration-tests/build.gradle
@@ -25,12 +25,24 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
id("AndroidXComposePlugin")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.compose.runtime.integrationtests"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+
+ compileSdk = 35
+ aarMetadata.minCompileSdk = 35
+ }
sourceSets {
commonMain {
@@ -92,10 +104,6 @@
}
}
-android {
- compileSdk 35
- namespace "androidx.compose.runtime.integrationtests"
-}
tasks.withType(KotlinCompile).configureEach {
kotlinOptions {
@@ -109,7 +117,7 @@
}
public File findFile() {
- project.file("src/androidAndroidTest/kotlin/androidx/compose/runtime/GroupSizeTests.kt")
+ project.file("src/androidInstrumentedTest/kotlin/androidx/compose/runtime/GroupSizeTests.kt")
}
class UpdateExpectedGroupSizes extends DefaultTask {
diff --git a/compose/ui/ui-geometry/build.gradle b/compose/ui/ui-geometry/build.gradle
index 552d9fb..b9b102b 100644
--- a/compose/ui/ui-geometry/build.gradle
+++ b/compose/ui/ui-geometry/build.gradle
@@ -28,13 +28,21 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
id("AndroidXComposePlugin")
}
-
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.compose.ui.geometry"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ }
jvmStubs()
linuxX64Stubs()
@@ -103,6 +111,3 @@
kotlinTarget = KotlinTarget.KOTLIN_1_9
}
-android {
- namespace "androidx.compose.ui.geometry"
-}
diff --git a/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayerTest.kt b/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayerTest.kt
index 6b44f1b..57085cf 100644
--- a/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayerTest.kt
+++ b/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayerTest.kt
@@ -51,7 +51,6 @@
import androidx.compose.ui.graphics.isLayerManagerInitialized
import androidx.compose.ui.graphics.isLayerPersistenceEnabled
import androidx.compose.ui.graphics.nativeCanvas
-import androidx.compose.ui.graphics.supportsV23RenderNode
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.graphics.toPixelMap
import androidx.compose.ui.unit.Density
@@ -1872,15 +1871,6 @@
)
}
- @Test
- fun testGraphicsLayerV23Supported() {
- assertTrue(supportsV23RenderNode("Samsung"))
- assertTrue(supportsV23RenderNode("Pixel"))
- assertFalse(supportsV23RenderNode("vivo"))
- assertFalse(supportsV23RenderNode("VIVO"))
- assertFalse(supportsV23RenderNode("viVO"))
- }
-
private class GraphicsContextHostDrawable(
val graphicsContext: GraphicsContext,
val block: DrawScope.(GraphicsContext) -> Unit
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidGraphicsContext.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidGraphicsContext.android.kt
index 9134ef7..ed2e9d0 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidGraphicsContext.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidGraphicsContext.android.kt
@@ -32,7 +32,6 @@
import androidx.compose.ui.graphics.layer.LayerManager
import androidx.compose.ui.graphics.layer.view.DrawChildContainer
import androidx.compose.ui.graphics.layer.view.ViewLayerContainer
-import java.util.Locale
/**
* Create a new [GraphicsContext] with the provided [ViewGroup] to contain [View] based layers.
@@ -202,7 +201,7 @@
}
internal companion object {
- var isRenderNodeCompatible: Boolean = supportsV23RenderNode(Build.MANUFACTURER)
+ var isRenderNodeCompatible: Boolean = true
const val enableLayerPersistence = false
}
@@ -218,10 +217,3 @@
internal val isLayerPersistenceEnabled: Boolean
get() = AndroidGraphicsContext.enableLayerPersistence
-
-internal fun supportsV23RenderNode(manufacturer: String): Boolean =
- // See b/371012452. Some Android devices don't support the reflective stub implementation
- // of RenderNode. More specifically the cast of android.graphics.Canvas to
- // android.view.DisplayListCanvas fails when trying to draw the layer itself.
- // In these cases we should fallback to using the View based layer implementation instead
- !manufacturer.lowercase(Locale.ENGLISH).contains("vivo")
diff --git a/compose/ui/ui-tooling-data/build.gradle b/compose/ui/ui-tooling-data/build.gradle
index f3ac046..472e819 100644
--- a/compose/ui/ui-tooling-data/build.gradle
+++ b/compose/ui/ui-tooling-data/build.gradle
@@ -28,12 +28,24 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
id("AndroidXComposePlugin")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.compose.ui.tooling.data"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+
+ compileSdk = 35
+ aarMetadata.minCompileSdk = 35
+ }
jvmStubs()
defaultPlatform(PlatformIdentifier.ANDROID)
@@ -111,7 +123,3 @@
kotlinTarget = KotlinTarget.KOTLIN_1_9
}
-android {
- compileSdk 35
- namespace "androidx.compose.ui.tooling.data"
-}
diff --git a/compose/ui/ui-tooling-preview/build.gradle b/compose/ui/ui-tooling-preview/build.gradle
index 8293248..2ea5618 100644
--- a/compose/ui/ui-tooling-preview/build.gradle
+++ b/compose/ui/ui-tooling-preview/build.gradle
@@ -27,11 +27,20 @@
plugins {
id("AndroidXPlugin")
id("AndroidXComposePlugin")
- id("com.android.library")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.compose.ui.tooling.preview"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ }
jvmStubs()
defaultPlatform(PlatformIdentifier.ANDROID)
@@ -93,6 +102,3 @@
metalavaK2UastEnabled = false
}
-android {
- namespace "androidx.compose.ui.tooling.preview"
-}
diff --git a/compose/ui/ui-unit/build.gradle b/compose/ui/ui-unit/build.gradle
index 9cd6331..ce761f72 100644
--- a/compose/ui/ui-unit/build.gradle
+++ b/compose/ui/ui-unit/build.gradle
@@ -28,12 +28,27 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
id("AndroidXComposePlugin")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.compose.ui.unit"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ optimization {
+ it.consumerKeepRules.publish = true
+ it.consumerKeepRules.files.add(
+ new File(project.projectDir, "proguard-rules.pro")
+ )
+ }
+ }
jvmStubs()
linuxX64Stubs()
@@ -116,9 +131,3 @@
kotlinTarget = KotlinTarget.KOTLIN_1_9
}
-android {
- namespace "androidx.compose.ui.unit"
- buildTypes.configureEach {
- consumerProguardFiles("proguard-rules.pro")
- }
-}
diff --git a/compose/ui/ui-util/build.gradle b/compose/ui/ui-util/build.gradle
index b1788829..f6ee5c2 100644
--- a/compose/ui/ui-util/build.gradle
+++ b/compose/ui/ui-util/build.gradle
@@ -27,12 +27,27 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
id("AndroidXComposePlugin")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.compose.ui.util"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ optimization {
+ it.consumerKeepRules.publish = true
+ it.consumerKeepRules.files.add(
+ new File(project.projectDir, "proguard-rules.pro")
+ )
+ }
+ }
jvmStubs()
linuxX64Stubs()
@@ -105,9 +120,3 @@
composeCompilerPluginEnabled = false
}
-android {
- namespace "androidx.compose.ui.util"
- buildTypes.configureEach {
- consumerProguardFiles("proguard-rules.pro")
- }
-}
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt
index bb7966b..1173955 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt
@@ -20,7 +20,6 @@
import android.view.MotionEvent.ACTION_HOVER_ENTER
import android.view.MotionEvent.ACTION_HOVER_EXIT
-import androidx.collection.IntObjectMap
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.InternalComposeUiApi
import androidx.compose.ui.Modifier
@@ -63,7 +62,6 @@
import androidx.compose.ui.platform.TextToolbar
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.platform.WindowInfo
-import androidx.compose.ui.semantics.SemanticsOwner
import androidx.compose.ui.spatial.RectManager
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
@@ -3419,9 +3417,6 @@
override val focusOwner: FocusOwner
get() = TODO("Not yet implemented")
- override val semanticsOwner: SemanticsOwner
- get() = TODO("Not yet implemented")
-
override val windowInfo: WindowInfo
get() = TODO("Not yet implemented")
@@ -3567,12 +3562,8 @@
}
override var measureIteration: Long = 0
-
override val viewConfiguration: ViewConfiguration
get() = TODO("Not yet implemented")
- override val layoutNodes: IntObjectMap<LayoutNode>
- get() = TODO("Not yet implemented")
-
override val sharedDrawScope = LayoutNodeDrawScope()
}
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
index 4c6c824..2552b929 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
@@ -21,8 +21,6 @@
import android.view.InputDevice
import android.view.KeyEvent as AndroidKeyEvent
import android.view.MotionEvent
-import androidx.collection.IntObjectMap
-import androidx.collection.intObjectMapOf
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.InternalComposeUiApi
import androidx.compose.ui.Modifier
@@ -59,8 +57,6 @@
import androidx.compose.ui.platform.TextToolbar
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.platform.WindowInfo
-import androidx.compose.ui.semantics.EmptySemanticsModifier
-import androidx.compose.ui.semantics.SemanticsOwner
import androidx.compose.ui.spatial.RectManager
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
@@ -2851,9 +2847,6 @@
override val rootForTest: RootForTest
get() = TODO("Not yet implemented")
- override val layoutNodes: IntObjectMap<LayoutNode>
- get() = TODO("Not yet implemented")
-
override val hapticFeedBack: HapticFeedback
get() = TODO("Not yet implemented")
@@ -2914,9 +2907,6 @@
override val focusOwner: FocusOwner
get() = TODO("Not yet implemented")
- override val semanticsOwner: SemanticsOwner =
- SemanticsOwner(root, EmptySemanticsModifier(), intObjectMapOf())
-
override val windowInfo: WindowInfo
get() = TODO("Not yet implemented")
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/Helpers.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/Helpers.kt
index 8295452..ca6ec66 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/Helpers.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/Helpers.kt
@@ -18,8 +18,6 @@
package androidx.compose.ui.layout
-import androidx.collection.IntObjectMap
-import androidx.collection.intObjectMapOf
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.InternalComposeUiApi
import androidx.compose.ui.autofill.Autofill
@@ -54,8 +52,6 @@
import androidx.compose.ui.platform.TextToolbar
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.platform.WindowInfo
-import androidx.compose.ui.semantics.EmptySemanticsModifier
-import androidx.compose.ui.semantics.SemanticsOwner
import androidx.compose.ui.spatial.RectManager
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
@@ -165,12 +161,7 @@
override fun onDetach(node: LayoutNode) {}
- override val root: LayoutNode = LayoutNode()
-
- override val semanticsOwner: SemanticsOwner =
- SemanticsOwner(root, EmptySemanticsModifier(), intObjectMapOf())
-
- override val layoutNodes: IntObjectMap<LayoutNode>
+ override val root: LayoutNode
get() = TODO("Not yet implemented")
override val sharedDrawScope: LayoutNodeDrawScope
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt
index 56e499a..fd30536 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt
@@ -18,8 +18,6 @@
package androidx.compose.ui.node
-import androidx.collection.IntObjectMap
-import androidx.collection.intObjectMapOf
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.InternalComposeUiApi
import androidx.compose.ui.Modifier
@@ -50,8 +48,6 @@
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.platform.WindowInfo
import androidx.compose.ui.platform.invertTo
-import androidx.compose.ui.semantics.EmptySemanticsModifier
-import androidx.compose.ui.semantics.SemanticsOwner
import androidx.compose.ui.spatial.RectManager
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
@@ -389,9 +385,6 @@
override val density: Density
get() = Density(1f)
- override val layoutNodes: IntObjectMap<LayoutNode>
- get() = TODO("Not yet implemented")
-
override val layoutDirection: LayoutDirection
get() = LayoutDirection.Ltr
@@ -428,9 +421,6 @@
override val focusOwner: FocusOwner
get() = TODO("Not yet implemented")
- override val semanticsOwner: SemanticsOwner =
- SemanticsOwner(root, EmptySemanticsModifier(), intObjectMapOf())
-
override val windowInfo: WindowInfo
get() = TODO("Not yet implemented")
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/semantics/SemanticsInfoTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/semantics/SemanticsInfoTest.kt
deleted file mode 100644
index 79bad61..0000000
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/semantics/SemanticsInfoTest.kt
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright 2024 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.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.node.RootForTest
-import androidx.compose.ui.platform.LocalView
-import androidx.compose.ui.semantics.SemanticsProperties.TestTag
-import androidx.compose.ui.test.junit4.ComposeContentTestRule
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithTag
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
-import com.google.common.truth.Correspondence
-import com.google.common.truth.Truth.assertThat
-import kotlin.test.Test
-import org.junit.Rule
-import org.junit.runner.RunWith
-
-@MediumTest
-@RunWith(AndroidJUnit4::class)
-class SemanticsInfoTest {
-
- @get:Rule val rule = createComposeRule()
-
- lateinit var semanticsOwner: SemanticsOwner
-
- @Test
- fun contentWithNoSemantics() {
- // Arrange.
- rule.setTestContent { Box {} }
- rule.waitForIdle()
-
- // Act.
- val rootSemantics = semanticsOwner.rootInfo
-
- // Assert.
- assertThat(rootSemantics).isNotNull()
- assertThat(rootSemantics.parentInfo).isNull()
- assertThat(rootSemantics.childrenInfo.size).isEqualTo(1)
-
- // Assert extension Functions.
- assertThat(rootSemantics.findSemanticsParent()).isNull()
- assertThat(rootSemantics.findMergingSemanticsParent()).isNull()
- assertThat(rootSemantics.findSemanticsChildren()).isEmpty()
- }
-
- @Test
- fun singleSemanticsModifier() {
- // Arrange.
- rule.setTestContent { Box(Modifier.semantics { this.testTag = "testTag" }) }
- rule.waitForIdle()
-
- // Act.
- val rootSemantics = semanticsOwner.rootInfo
- val semantics = rule.getSemanticsInfoForTag("testTag")!!
-
- // Assert.
- assertThat(rootSemantics.parentInfo).isNull()
- assertThat(rootSemantics.childrenInfo.asMutableList()).containsExactly(semantics)
-
- assertThat(semantics.parentInfo).isEqualTo(rootSemantics)
- assertThat(semantics.childrenInfo.size).isEqualTo(0)
-
- // Assert extension Functions.
- assertThat(rootSemantics.findSemanticsParent()).isNull()
- assertThat(rootSemantics.findMergingSemanticsParent()).isNull()
- assertThat(rootSemantics.findSemanticsChildren().map { it.semanticsConfiguration })
- .comparingElementsUsing(SemanticsConfigurationComparator)
- .containsExactly(SemanticsConfiguration().apply { testTag = "testTag" })
-
- assertThat(semantics.findSemanticsParent()).isEqualTo(rootSemantics)
- assertThat(semantics.findMergingSemanticsParent()).isNull()
- assertThat(semantics.findSemanticsChildren()).isEmpty()
- }
-
- @Test
- fun twoSemanticsModifiers() {
- // Arrange.
- rule.setTestContent {
- Box(Modifier.semantics { this.testTag = "item1" })
- Box(Modifier.semantics { this.testTag = "item2" })
- }
- rule.waitForIdle()
-
- // Act.
- val rootSemantics: SemanticsInfo = semanticsOwner.rootInfo
- val semantics1 = rule.getSemanticsInfoForTag("item1")
- val semantics2 = rule.getSemanticsInfoForTag("item2")
-
- // Assert.
- assertThat(rootSemantics.parentInfo).isNull()
- assertThat(rootSemantics.childrenInfo.map { it.semanticsConfiguration }.toList())
- .comparingElementsUsing(SemanticsConfigurationComparator)
- .containsExactly(
- SemanticsConfiguration().apply { testTag = "item1" },
- SemanticsConfiguration().apply { testTag = "item2" }
- )
- .inOrder()
-
- assertThat(rootSemantics.findSemanticsChildren().map { it.semanticsConfiguration })
- .comparingElementsUsing(SemanticsConfigurationComparator)
- .containsExactly(
- SemanticsConfiguration().apply { testTag = "item1" },
- SemanticsConfiguration().apply { testTag = "item2" }
- )
- .inOrder()
-
- checkNotNull(semantics1)
- assertThat(semantics1.parentInfo).isEqualTo(rootSemantics)
- assertThat(semantics1.childrenInfo.size).isEqualTo(0)
-
- checkNotNull(semantics2)
- assertThat(semantics2.parentInfo).isEqualTo(rootSemantics)
- assertThat(semantics2.childrenInfo.size).isEqualTo(0)
-
- // Assert extension Functions.
- assertThat(rootSemantics.findSemanticsParent()).isNull()
- assertThat(rootSemantics.findMergingSemanticsParent()).isNull()
- assertThat(rootSemantics.findSemanticsChildren().map { it.semanticsConfiguration })
- .comparingElementsUsing(SemanticsConfigurationComparator)
- .containsExactly(
- SemanticsConfiguration().apply { testTag = "item1" },
- SemanticsConfiguration().apply { testTag = "item2" }
- )
- .inOrder()
-
- assertThat(semantics1.findSemanticsParent()).isEqualTo(rootSemantics)
- assertThat(semantics1.findMergingSemanticsParent()).isNull()
- assertThat(semantics1.findSemanticsChildren()).isEmpty()
-
- assertThat(semantics2.findSemanticsParent()).isEqualTo(rootSemantics)
- assertThat(semantics2.findMergingSemanticsParent()).isNull()
- assertThat(semantics2.findSemanticsChildren()).isEmpty()
- }
-
- // TODO(ralu): Split this into multiple tests.
- @Test
- fun nodeDeepInHierarchy() {
- // Arrange.
- rule.setTestContent {
- Column(Modifier.semantics(mergeDescendants = true) { testTag = "outerColumn" }) {
- Row(Modifier.semantics { testTag = "outerRow" }) {
- Column(Modifier.semantics(mergeDescendants = true) { testTag = "column" }) {
- Row(Modifier.semantics { testTag = "row" }) {
- Column {
- Box(Modifier.semantics { testTag = "box" })
- Row(
- Modifier.semantics {}
- .semantics { testTag = "testTarget" }
- .semantics { testTag = "extra modifier2" }
- ) {
- Box { Box(Modifier.semantics { testTag = "child1" }) }
- Box(Modifier.semantics { testTag = "child2" }) {
- Box(Modifier.semantics { testTag = "grandChild" })
- }
- Box {}
- Row {
- Box {}
- Box {}
- }
- Box { Box(Modifier.semantics { testTag = "child3" }) }
- }
- }
- }
- }
- }
- }
- }
- rule.waitForIdle()
- val row = rule.getSemanticsInfoForTag(tag = "row", useUnmergedTree = true)
- val column = rule.getSemanticsInfoForTag("column")
-
- // Act.
- val testTarget = rule.getSemanticsInfoForTag(tag = "testTarget", useUnmergedTree = true)
-
- // Assert.
- checkNotNull(testTarget)
- assertThat(testTarget.parentInfo).isNotEqualTo(row)
- assertThat(testTarget.findSemanticsParent()).isEqualTo(row)
- assertThat(testTarget.findMergingSemanticsParent()).isEqualTo(column)
- assertThat(testTarget.childrenInfo.size).isEqualTo(5)
- assertThat(testTarget.findSemanticsChildren().map { it.semanticsConfiguration })
- .comparingElementsUsing(SemanticsConfigurationComparator)
- .containsExactly(
- SemanticsConfiguration().apply { testTag = "child1" },
- SemanticsConfiguration().apply { testTag = "child2" },
- SemanticsConfiguration().apply { testTag = "child3" }
- )
- .inOrder()
- assertThat(testTarget.semanticsConfiguration?.getOrNull(TestTag)).isEqualTo("testTarget")
- }
-
- private fun ComposeContentTestRule.setTestContent(composable: @Composable () -> Unit) {
- setContent {
- semanticsOwner = (LocalView.current as RootForTest).semanticsOwner
- composable()
- }
- }
-
- /** Helper function that returns a list of children that is easier to assert on in tests. */
- private fun SemanticsInfo.findSemanticsChildren(): List<SemanticsInfo> {
- val children = mutableListOf<SemanticsInfo>()
- [email protected] { children.add(it) }
- return children
- }
-
- private fun ComposeContentTestRule.getSemanticsInfoForTag(
- tag: String,
- useUnmergedTree: Boolean = true
- ): SemanticsInfo? {
- return semanticsOwner[onNodeWithTag(tag, useUnmergedTree).semanticsId()]
- }
-
- companion object {
- private val SemanticsConfigurationComparator =
- Correspondence.from<SemanticsConfiguration, SemanticsConfiguration>(
- { actual, expected ->
- actual != null &&
- expected != null &&
- actual.getOrNull(TestTag) == expected.getOrNull(TestTag)
- },
- "has same test tag as "
- )
- }
-}
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/semantics/SemanticsListenerTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/semantics/SemanticsListenerTest.kt
deleted file mode 100644
index 77965fe..0000000
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/semantics/SemanticsListenerTest.kt
+++ /dev/null
@@ -1,556 +0,0 @@
-/*
- * Copyright 2024 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.foundation.border
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.BoxScope
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.size
-import androidx.compose.material.Text
-import androidx.compose.material.TextField
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.ComposeUiFlags
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.isExactly
-import androidx.compose.ui.focus.onFocusChanged
-import androidx.compose.ui.graphics.Color.Companion.Black
-import androidx.compose.ui.graphics.Color.Companion.Red
-import androidx.compose.ui.node.RootForTest
-import androidx.compose.ui.node.SemanticsModifierNode
-import androidx.compose.ui.node.invalidateSemantics
-import androidx.compose.ui.platform.LocalView
-import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.SemanticsNodeInteraction
-import androidx.compose.ui.test.junit4.ComposeContentTestRule
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithTag
-import androidx.compose.ui.test.requestFocus
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.util.fastJoinToString
-import androidx.test.filters.MediumTest
-import com.google.common.truth.Truth.assertThat
-import kotlin.test.Test
-import org.junit.Before
-import org.junit.Rule
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-
-@MediumTest
-@RunWith(Parameterized::class)
-class SemanticsListenerTest(private val isSemanticAutofillEnabled: Boolean) {
-
- @get:Rule val rule = createComposeRule()
-
- private lateinit var semanticsOwner: SemanticsOwner
-
- companion object {
- @JvmStatic
- @Parameterized.Parameters(name = "isSemanticAutofillEnabled = {0}")
- fun initParameters() = listOf(false, true)
- }
-
- @Before
- fun setup() {
- @OptIn(ExperimentalComposeUiApi::class)
- ComposeUiFlags.isSemanticAutofillEnabled = isSemanticAutofillEnabled
- }
-
- // Initial layout does not trigger listeners. Users have to detect the initial semantics
- // values by detecting first layout (You can get the bounds from RectManager.RectList).
- @Test
- fun initialComposition_doesNotTriggerListeners() {
- // Arrange.
- val events = mutableListOf<Event<String>>()
- rule.setTestContent(
- onSemanticsChange = { info, prev ->
- events.add(Event(info.semanticsId, prev?.Text, info.semanticsConfiguration?.Text))
- }
- ) {
- Text(text = "text")
- }
-
- // Assert.
- rule.runOnIdle { assertThat(events).isEmpty() }
- }
-
- @Test
- fun addingNonSemanticsModifier() {
- // Arrange.
- val events = mutableListOf<Event<String>>()
- var addModifier by mutableStateOf(false)
- val text = AnnotatedString("text")
- rule.setTestContent(
- onSemanticsChange = { info, prev ->
- events.add(Event(info.semanticsId, prev?.Text, info.semanticsConfiguration?.Text))
- }
- ) {
- Box(
- modifier =
- Modifier.then(if (addModifier) Modifier.size(1000.dp) else Modifier)
- .semantics { this.text = text }
- .testTag("item")
- )
- }
-
- // Act.
- rule.runOnIdle { addModifier = true }
-
- // Assert.
- rule.runOnIdle { assertThat(events).isEmpty() }
- }
-
- @Test
- fun removingNonSemanticsModifier() {
- // Arrange.
- val events = mutableListOf<Event<String>>()
- var removeModifier by mutableStateOf(false)
- val text = AnnotatedString("text")
- rule.setTestContent(
- onSemanticsChange = { info, prev ->
- events.add(Event(info.semanticsId, prev?.Text, info.semanticsConfiguration?.Text))
- }
- ) {
- Box(
- modifier =
- Modifier.then(if (removeModifier) Modifier else Modifier.size(1000.dp))
- .semantics { this.text = text }
- .testTag("item")
- )
- }
-
- // Act.
- rule.runOnIdle { removeModifier = true }
-
- // Assert.
- rule.runOnIdle { assertThat(events).isEmpty() }
- }
-
- @Test
- fun addingSemanticsModifier() {
- // Arrange.
- val events = mutableListOf<Event<String>>()
- var addModifier by mutableStateOf(false)
- val text = AnnotatedString("text")
- rule.setTestContent(
- onSemanticsChange = { info, prev ->
- events.add(Event(info.semanticsId, prev?.Text, info.semanticsConfiguration?.Text))
- }
- ) {
- Box(
- modifier =
- Modifier.size(100.dp)
- .then(
- if (addModifier) Modifier.semantics { this.text = text } else Modifier
- )
- .testTag("item")
- )
- }
-
- // Act.
- rule.runOnIdle { addModifier = true }
-
- // Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
- rule.runOnIdle {
- if (isSemanticAutofillEnabled) {
- assertThat(events)
- .isExactly(Event(semanticsId, prevSemantics = null, newSemantics = "text"))
- } else {
- assertThat(events).isEmpty()
- }
- }
- }
-
- @Test
- fun removingSemanticsModifier() {
- // Arrange.
- val events = mutableListOf<Event<String>>()
- var removeModifier by mutableStateOf(false)
- val text = AnnotatedString("text")
- rule.setTestContent(
- onSemanticsChange = { info, prev ->
- events.add(Event(info.semanticsId, prev?.Text, info.semanticsConfiguration?.Text))
- }
- ) {
- Box(
- modifier =
- Modifier.size(1000.dp)
- .then(
- if (removeModifier) Modifier
- else Modifier.semantics { this.text = text }
- )
- .testTag("item")
- )
- }
-
- // Act.
- rule.runOnIdle { removeModifier = true }
-
- // Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
- rule.runOnIdle {
- if (isSemanticAutofillEnabled) {
- assertThat(events)
- .isExactly(Event(semanticsId, prevSemantics = "text", newSemantics = null))
- } else {
- assertThat(events).isEmpty()
- }
- }
- }
-
- @Test
- fun changingMutableSemanticsProperty() {
- // Arrange.
- val events = mutableListOf<Event<String>>()
- var text by mutableStateOf(AnnotatedString("text1"))
- rule.setTestContent(
- onSemanticsChange = { info, prev ->
- events.add(Event(info.semanticsId, prev?.Text, info.semanticsConfiguration?.Text))
- }
- ) {
- Box(modifier = Modifier.semantics { this.text = text }.testTag("item"))
- }
-
- // Act.
- rule.runOnIdle { text = AnnotatedString("text2") }
-
- // Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
- rule.runOnIdle {
- if (isSemanticAutofillEnabled) {
- assertThat(events)
- .isExactly(Event(semanticsId, prevSemantics = "text1", newSemantics = "text2"))
- } else {
- assertThat(events).isEmpty()
- }
- }
- }
-
- @Test
- fun changingMutableSemanticsProperty_alongWithRecomposition() {
- // Arrange.
- val events = mutableListOf<Event<String>>()
- var text by mutableStateOf(AnnotatedString("text1"))
- rule.setTestContent(
- onSemanticsChange = { info, prev ->
- events.add(Event(info.semanticsId, prev?.Text, info.semanticsConfiguration?.Text))
- }
- ) {
- Box(
- modifier =
- Modifier.border(2.dp, if (text.text == "text1") Red else Black)
- .semantics { this.text = text }
- .testTag("item")
- )
- }
-
- // Act.
- rule.runOnIdle { text = AnnotatedString("text2") }
-
- // Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
- rule.runOnIdle {
- if (isSemanticAutofillEnabled) {
- assertThat(events)
- .isExactly(Event(semanticsId, prevSemantics = "text1", newSemantics = "text2"))
- } else {
- assertThat(events).isEmpty()
- }
- }
- }
-
- @Test
- fun changingSemanticsProperty_andCallingInvalidateSemantics() {
- // Arrange.
- val events = mutableListOf<Event<String>>()
- val modifierNode =
- object : SemanticsModifierNode, Modifier.Node() {
- override fun SemanticsPropertyReceiver.applySemantics() {}
- }
- var text = AnnotatedString("text1")
- rule.setTestContent(
- onSemanticsChange = { info, prev ->
- events.add(Event(info.semanticsId, prev?.Text, info.semanticsConfiguration?.Text))
- }
- ) {
- Box(
- modifier =
- Modifier.elementFor(modifierNode).semantics { this.text = text }.testTag("item")
- )
- }
-
- // Act.
- rule.runOnIdle {
- text = AnnotatedString("text2")
- modifierNode.invalidateSemantics()
- }
-
- // Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
- rule.runOnIdle {
- if (isSemanticAutofillEnabled) {
- assertThat(events)
- .isExactly(Event(semanticsId, prevSemantics = "text1", newSemantics = "text2"))
- } else {
- assertThat(events).isEmpty()
- }
- }
- }
-
- @Test
- fun textChange() {
- // Arrange.
- val events = mutableListOf<Event<String>>()
- var text by mutableStateOf("text1")
- rule.setTestContent(
- onSemanticsChange = { info, prev ->
- events.add(Event(info.semanticsId, prev?.Text, info.semanticsConfiguration?.Text))
- }
- ) {
- Text(text = text, modifier = Modifier.testTag("item"))
- }
-
- // Act.
- rule.runOnIdle { text = "text2" }
-
- // Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
- rule.runOnIdle {
- if (isSemanticAutofillEnabled) {
- assertThat(events)
- .isExactly(Event(semanticsId, prevSemantics = "text1", newSemantics = "text2"))
- } else {
- assertThat(events).isEmpty()
- }
- }
- }
-
- @Test
- fun multipleTextChanges() {
- // Arrange.
- val events = mutableListOf<Event<String>>()
- var text by mutableStateOf("text1")
- rule.setTestContent(
- onSemanticsChange = { info, prev ->
- events.add(Event(info.semanticsId, prev?.Text, info.semanticsConfiguration?.Text))
- }
- ) {
- Text(text = text, modifier = Modifier.testTag("item"))
- }
-
- // Act.
- rule.runOnIdle { text = "text2" }
- rule.runOnIdle { text = "text3" }
-
- // Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
- rule.runOnIdle {
- if (isSemanticAutofillEnabled) {
- assertThat(events)
- .isExactly(
- Event(semanticsId, prevSemantics = "text1", newSemantics = "text2"),
- Event(semanticsId, prevSemantics = "text2", newSemantics = "text3")
- )
- } else {
- assertThat(events).isEmpty()
- }
- }
- }
-
- @Test
- fun EditTextChange() {
- // Arrange.
- val events = mutableListOf<Event<String>>()
- var text by mutableStateOf("text1")
- rule.setTestContent(
- onSemanticsChange = { info, prev ->
- events.add(
- Event(
- info.semanticsId,
- prev?.EditableText,
- info.semanticsConfiguration?.EditableText
- )
- )
- }
- ) {
- TextField(
- value = text,
- onValueChange = { text = it },
- modifier = Modifier.testTag("item")
- )
- }
-
- // Act.
- rule.runOnIdle { text = "text2" }
-
- // Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
- rule.runOnIdle {
- if (isSemanticAutofillEnabled) {
- assertThat(events)
- .isExactly(Event(semanticsId, prevSemantics = "text1", newSemantics = "text2"))
- } else {
- assertThat(events).isEmpty()
- }
- }
- }
-
- @Test
- fun FocusChange_withNoRecomposition() {
- // Arrange.
- val events = mutableListOf<Event<Boolean>>()
- rule.setTestContent(
- onSemanticsChange = { info, prev ->
- events.add(
- Event(
- info.semanticsId,
- prev?.getOrNull(SemanticsProperties.Focused),
- info.semanticsConfiguration?.getOrNull(SemanticsProperties.Focused)
- )
- )
- }
- ) {
- Column {
- Box(Modifier.testTag("item1").size(100.dp).focusable())
- Box(Modifier.testTag("item2").size(100.dp).focusable())
- }
- }
- rule.onNodeWithTag("item1").requestFocus()
- rule.runOnIdle { events.clear() }
-
- // Act.
- rule.onNodeWithTag("item2").requestFocus()
-
- // Assert.
- val item1 = rule.onNodeWithTag("item1").semanticsId
- val item2 = rule.onNodeWithTag("item2").semanticsId
- rule.runOnIdle {
- if (isSemanticAutofillEnabled) {
- assertThat(events)
- .isExactly(
- Event(item1, prevSemantics = true, newSemantics = false),
- Event(item2, prevSemantics = false, newSemantics = true)
- )
- } else {
- assertThat(events).isEmpty()
- }
- }
- }
-
- @Test
- fun FocusChange_thatCausesRecomposition() {
- // Arrange.
- val events = mutableListOf<Event<Boolean>>()
- rule.setTestContent(
- onSemanticsChange = { info, prev ->
- events.add(
- Event(
- info.semanticsId,
- prev?.getOrNull(SemanticsProperties.Focused),
- info.semanticsConfiguration?.getOrNull(SemanticsProperties.Focused)
- )
- )
- }
- ) {
- Column {
- FocusableBox(Modifier.testTag("item1"))
- FocusableBox(Modifier.testTag("item2"))
- }
- }
- rule.onNodeWithTag("item1").requestFocus()
- rule.runOnIdle { events.clear() }
-
- // Act.
- rule.onNodeWithTag("item2").requestFocus()
-
- // Assert.
- val item1 = rule.onNodeWithTag("item1").semanticsId
- val item2 = rule.onNodeWithTag("item2").semanticsId
- rule.runOnIdle {
- if (isSemanticAutofillEnabled) {
- assertThat(events)
- .isExactly(
- Event(item1, prevSemantics = true, newSemantics = false),
- Event(item2, prevSemantics = false, newSemantics = true)
- )
- } else {
- assertThat(events).isEmpty()
- }
- }
- }
-
- private val SemanticsConfiguration.Text
- get() = getOrNull(SemanticsProperties.Text)?.fastJoinToString()
-
- private val SemanticsConfiguration.EditableText
- get() = getOrNull(SemanticsProperties.EditableText)?.toString()
-
- private fun ComposeContentTestRule.setTestContent(
- onSemanticsChange: (SemanticsInfo, SemanticsConfiguration?) -> Unit,
- composable: @Composable () -> Unit
- ) {
- val semanticsListener =
- object : SemanticsListener {
- override fun onSemanticsChanged(
- semanticsInfo: SemanticsInfo,
- previousSemanticsConfiguration: SemanticsConfiguration?
- ) {
- onSemanticsChange(semanticsInfo, previousSemanticsConfiguration)
- }
- }
- setContent {
- semanticsOwner = (LocalView.current as RootForTest).semanticsOwner
- DisposableEffect(semanticsOwner) {
- semanticsOwner.listeners.add(semanticsListener)
- onDispose { semanticsOwner.listeners.remove(semanticsListener) }
- }
- composable()
- }
- }
-
- data class Event<T>(val semanticsId: Int, val prevSemantics: T?, val newSemantics: T?)
-
- // TODO(b/272068594): Add api to fetch the semantics id from SemanticsNodeInteraction directly.
- private val SemanticsNodeInteraction.semanticsId: Int
- get() = fetchSemanticsNode().id
-
- @Composable
- private fun FocusableBox(
- modifier: Modifier = Modifier,
- content: @Composable BoxScope.() -> Unit = {}
- ) {
- var borderColor by remember { mutableStateOf(Black) }
- Box(
- modifier =
- modifier
- .size(100.dp)
- .onFocusChanged { borderColor = if (it.isFocused) Red else Black }
- .border(2.dp, borderColor)
- .focusable(),
- content = content
- )
- }
-}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillManager.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillManager.android.kt
index b86440d..ff82cbf 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillManager.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillManager.android.kt
@@ -298,8 +298,6 @@
SemanticsContentDataType
)
}
- // TODO(b/138549623): Instead of creating a flattened tree by using the nodes from the map, we
- // can use SemanticsOwner to get the root SemanticsInfo and create a more representative tree.
var index = AutofillApi26Helper.addChildCount(root, count)
// Iterate through currentSemanticsNodes, finding autofill-related nodes
@@ -479,7 +477,7 @@
}
@RequiresApi(Build.VERSION_CODES.O)
-private class AutofillManagerWrapperImpl(val view: View) : AutofillManagerWrapper {
+private class AutofillManagerWrapperImpl(val view: AndroidComposeView) : AutofillManagerWrapper {
override val autofillManager =
view.context.getSystemService(PlatformAndroidManager::class.java)
?: error("Autofill service could not be located.")
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
index 7dc9d64..dfac967 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
@@ -63,8 +63,6 @@
import androidx.annotation.DoNotInline
import androidx.annotation.RequiresApi
import androidx.annotation.VisibleForTesting
-import androidx.collection.MutableIntObjectMap
-import androidx.collection.mutableIntObjectMapOf
import androidx.compose.runtime.collection.mutableVectorOf
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
@@ -431,12 +429,9 @@
.then(dragAndDropManager.modifier)
}
- override val layoutNodes: MutableIntObjectMap<LayoutNode> = mutableIntObjectMapOf()
-
override val rootForTest: RootForTest = this
- override val semanticsOwner: SemanticsOwner =
- SemanticsOwner(root, rootSemanticsNode, layoutNodes)
+ override val semanticsOwner: SemanticsOwner = SemanticsOwner(root, rootSemanticsNode)
private val composeAccessibilityDelegate = AndroidComposeViewAccessibilityDelegateCompat(this)
internal var contentCaptureManager =
AndroidContentCaptureManager(
@@ -1031,12 +1026,9 @@
composeAccessibilityDelegate.SendRecurringAccessibilityEventsIntervalMillis = intervalMillis
}
- override fun onAttach(node: LayoutNode) {
- layoutNodes[node.semanticsId] = node
- }
+ override fun onAttach(node: LayoutNode) {}
override fun onDetach(node: LayoutNode) {
- layoutNodes.remove(node.semanticsId)
measureAndLayoutDelegate.onNodeDetached(node)
requestClearInvalidObservations()
@OptIn(ExperimentalComposeUiApi::class)
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 7deeca8..7036817 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
@@ -2355,11 +2355,11 @@
if (layoutNode.nodes.has(Nodes.Semantics)) layoutNode
else layoutNode.findClosestParentNode { it.nodes.has(Nodes.Semantics) }
- val config = semanticsNode?.semanticsConfiguration ?: return
+ val config = semanticsNode?.collapsedSemantics ?: return
if (!config.isMergingSemanticsOfDescendants) {
semanticsNode
.findClosestParentNode {
- it.semanticsConfiguration?.isMergingSemanticsOfDescendants == true
+ it.collapsedSemantics?.isMergingSemanticsOfDescendants == true
}
?.let { semanticsNode = it }
}
@@ -3264,12 +3264,12 @@
val ancestor =
layoutNode.findClosestParentNode {
// looking for text field merging node
- val ancestorSemanticsConfiguration = it.semanticsConfiguration
+ val ancestorSemanticsConfiguration = it.collapsedSemantics
ancestorSemanticsConfiguration?.isMergingSemanticsOfDescendants == true &&
ancestorSemanticsConfiguration.contains(SemanticsProperties.EditableText)
}
return ancestor != null &&
- ancestor.semanticsConfiguration?.getOrNull(SemanticsProperties.Focused) != true
+ ancestor.collapsedSemantics?.getOrNull(SemanticsProperties.Focused) != true
}
private fun AccessibilityAction<*>.accessibilityEquals(other: Any?): Boolean {
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ComposeView.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ComposeView.android.kt
index d2ab361..0b66d5d 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ComposeView.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ComposeView.android.kt
@@ -60,6 +60,7 @@
init {
clipChildren = false
clipToPadding = false
+ importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_YES
}
/**
diff --git a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
index c884b74..1f96189 100644
--- a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
+++ b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
@@ -17,8 +17,6 @@
package androidx.compose.ui.node
-import androidx.collection.IntObjectMap
-import androidx.collection.intObjectMapOf
import androidx.compose.testutils.TestViewConfiguration
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.InternalComposeUiApi
@@ -68,10 +66,8 @@
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.platform.WindowInfo
import androidx.compose.ui.platform.invertTo
-import androidx.compose.ui.semantics.EmptySemanticsModifier
import androidx.compose.ui.semantics.SemanticsConfiguration
import androidx.compose.ui.semantics.SemanticsModifier
-import androidx.compose.ui.semantics.SemanticsOwner
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.spatial.RectManager
import androidx.compose.ui.text.font.Font
@@ -2318,8 +2314,6 @@
internal class MockOwner(
private val position: IntOffset = IntOffset.Zero,
override val root: LayoutNode = LayoutNode(),
- override val semanticsOwner: SemanticsOwner =
- SemanticsOwner(root, EmptySemanticsModifier(), intObjectMapOf()),
override val coroutineContext: CoroutineContext =
Executors.newFixedThreadPool(3).asCoroutineDispatcher()
) : Owner {
@@ -2553,9 +2547,6 @@
override val viewConfiguration: ViewConfiguration
get() = TODO("Not yet implemented")
- override val layoutNodes: IntObjectMap<LayoutNode>
- get() = TODO("Not yet implemented")
-
override val sharedDrawScope = LayoutNodeDrawScope()
}
diff --git a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/ModifierLocalConsumerEntityTest.kt b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/ModifierLocalConsumerEntityTest.kt
index bc17a55..8c77a83 100644
--- a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/ModifierLocalConsumerEntityTest.kt
+++ b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/ModifierLocalConsumerEntityTest.kt
@@ -18,8 +18,6 @@
package androidx.compose.ui.node
-import androidx.collection.IntObjectMap
-import androidx.collection.intObjectMapOf
import androidx.compose.runtime.collection.mutableVectorOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -52,8 +50,6 @@
import androidx.compose.ui.platform.TextToolbar
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.platform.WindowInfo
-import androidx.compose.ui.semantics.EmptySemanticsModifier
-import androidx.compose.ui.semantics.SemanticsOwner
import androidx.compose.ui.spatial.RectManager
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
@@ -337,9 +333,7 @@
override fun onDetach(node: LayoutNode) {}
- override val root: LayoutNode = LayoutNode()
-
- override val layoutNodes: IntObjectMap<LayoutNode>
+ override val root: LayoutNode
get() = TODO("Not yet implemented")
override val sharedDrawScope: LayoutNodeDrawScope
@@ -381,9 +375,6 @@
override val focusOwner: FocusOwner
get() = TODO("Not yet implemented")
- override val semanticsOwner: SemanticsOwner =
- SemanticsOwner(root, EmptySemanticsModifier(), intObjectMapOf())
-
override val windowInfo: WindowInfo
get() = TODO("Not yet implemented")
@@ -451,7 +442,7 @@
override fun forceMeasureTheSubtree(layoutNode: LayoutNode, affectsLookahead: Boolean) =
TODO("Not yet implemented")
- override fun onSemanticsChange() {}
+ override fun onSemanticsChange() = TODO("Not yet implemented")
override fun onLayoutChange(layoutNode: LayoutNode) = TODO("Not yet implemented")
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
index ace8eed..2da7de0 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
@@ -19,8 +19,6 @@
import androidx.compose.runtime.CompositionLocalMap
import androidx.compose.runtime.collection.MutableVector
import androidx.compose.runtime.collection.mutableVectorOf
-import androidx.compose.ui.ComposeUiFlags
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.InternalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
@@ -56,7 +54,6 @@
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.platform.simpleIdentityToString
import androidx.compose.ui.semantics.SemanticsConfiguration
-import androidx.compose.ui.semantics.SemanticsInfo
import androidx.compose.ui.semantics.generateSemanticsId
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
@@ -91,7 +88,6 @@
Remeasurement,
OwnerScope,
LayoutInfo,
- SemanticsInfo,
ComposeUiNode,
InteroperableComposeUiNode,
Owner.OnLayoutCompletedListener {
@@ -398,62 +394,43 @@
invalidateMeasurements()
}
- private var _semanticsConfiguration: SemanticsConfiguration? = null
- override val semanticsConfiguration: SemanticsConfiguration?
- get() {
- // This is needed until we completely move to the new world where we always pre-compute
- // the semantics configuration. At that point, this can be replaced by
- // check(!isSemanticsInvalidated) or remove this custom getter.
- if (isSemanticsInvalidated) {
- _semanticsConfiguration = calculateSemanticsConfiguration()
- }
- return _semanticsConfiguration
- }
-
- private fun calculateSemanticsConfiguration(): SemanticsConfiguration? {
- if (!nodes.has(Nodes.Semantics)) return null
-
- var config = SemanticsConfiguration()
- requireOwner().snapshotObserver.observeSemanticsReads(this) {
- nodes.tailToHead(Nodes.Semantics) {
- if (it.shouldClearDescendantSemantics) {
- config = SemanticsConfiguration()
- config.isClearingSemantics = true
- }
- if (it.shouldMergeDescendantSemantics) {
- config.isMergingSemanticsOfDescendants = true
- }
- with(config) { with(it) { applySemantics() } }
- }
- }
- return config
- }
-
- private var isSemanticsInvalidated = false
+ private var _collapsedSemantics: SemanticsConfiguration? = null
internal fun invalidateSemantics() {
- if (
- @OptIn(ExperimentalComposeUiApi::class) !ComposeUiFlags.isSemanticAutofillEnabled ||
- nodes.isUpdating ||
- applyingModifierOnAttach
- ) {
- // We are currently updating the modifier, so just schedule an invalidation. After
- // applying the modifier, we will notify listeners of semantics changes.
- isSemanticsInvalidated = true
- } else {
- // We are not currently updating the modifier, so instead of scheduling invalidation,
- // we update the semantics configuration and send the notification event right away.
- val prev = _semanticsConfiguration
- _semanticsConfiguration = calculateSemanticsConfiguration()
- requireOwner().semanticsOwner.notifySemanticsChange(this, prev)
- }
-
+ _collapsedSemantics = null
// TODO(lmr): this ends up scheduling work that diffs the entire tree, but we should
// eventually move to marking just this node as invalidated since we are invalidating
// on a per-node level. This should preserve current behavior for now.
requireOwner().onSemanticsChange()
}
+ internal val collapsedSemantics: SemanticsConfiguration?
+ get() {
+ // TODO: investigate if there's a better way to approach "half attached" state and
+ // whether or not deactivated nodes should be considered removed or not.
+ if (!isAttached || isDeactivated) return null
+
+ if (!nodes.has(Nodes.Semantics) || _collapsedSemantics != null) {
+ return _collapsedSemantics
+ }
+
+ var config = SemanticsConfiguration()
+ requireOwner().snapshotObserver.observeSemanticsReads(this) {
+ nodes.tailToHead(Nodes.Semantics) {
+ if (it.shouldClearDescendantSemantics) {
+ config = SemanticsConfiguration()
+ config.isClearingSemantics = true
+ }
+ if (it.shouldMergeDescendantSemantics) {
+ config.isMergingSemanticsOfDescendants = true
+ }
+ with(config) { with(it) { applySemantics() } }
+ }
+ }
+ _collapsedSemantics = config
+ return config
+ }
+
/**
* Set the [Owner] of this LayoutNode. This LayoutNode must not already be attached. [owner]
* must match its [parent].[owner].
@@ -486,7 +463,9 @@
pendingModifier?.let { applyModifier(it) }
pendingModifier = null
- if (nodes.has(Nodes.Semantics)) invalidateSemantics()
+ if (nodes.has(Nodes.Semantics)) {
+ invalidateSemantics()
+ }
owner.onAttach(this)
// Update lookahead root when attached. For nested cases, we'll always use the
@@ -538,9 +517,9 @@
}
layoutDelegate.resetAlignmentLines()
onDetach?.invoke(owner)
+
if (nodes.has(Nodes.Semantics)) {
- _semanticsConfiguration = null
- requireOwner().onSemanticsChange()
+ invalidateSemantics()
}
nodes.runDetachLifecycle()
ignoreRemeasureRequests { _foldedChildren.forEach { child -> child.detach() } }
@@ -576,10 +555,6 @@
return _zSortedChildren
}
- @Suppress("UNCHECKED_CAST")
- override val childrenInfo: MutableVector<SemanticsInfo>
- get() = zSortedChildren as MutableVector<SemanticsInfo>
-
override val isValidOwnerScope: Boolean
get() = isAttached
@@ -891,14 +866,6 @@
if (lookaheadRoot == null && nodes.has(Nodes.ApproachMeasure)) {
lookaheadRoot = this
}
- // Notify semantics listeners if semantics was invalidated.
- @OptIn(ExperimentalComposeUiApi::class)
- if (ComposeUiFlags.isSemanticAutofillEnabled && isSemanticsInvalidated) {
- val prev = _semanticsConfiguration
- _semanticsConfiguration = calculateSemanticsConfiguration()
- isSemanticsInvalidated = false
- requireOwner().semanticsOwner.notifySemanticsChange(this, prev)
- }
}
private fun resetModifierState() {
@@ -1303,7 +1270,7 @@
}
}
- override val parentInfo: SemanticsInfo?
+ override val parentInfo: LayoutInfo?
get() = parent
override var isDeactivated = false
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt
index c08a929..ff19036 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt
@@ -40,8 +40,8 @@
internal var head: Modifier.Node = tail
private set
- internal val isUpdating: Boolean
- get() = head.parent != null
+ private val isUpdating: Boolean
+ get() = head === SentinelHead
private val aggregateChildKindSet: Int
get() = head.aggregateChildKindSet
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt
index f102b02..0ce80dd 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt
@@ -1512,7 +1512,7 @@
override fun interceptOutOfBoundsChildEvents(node: Modifier.Node) = false
override fun shouldHitTestChildren(parentLayoutNode: LayoutNode) =
- parentLayoutNode.semanticsConfiguration?.isClearingSemantics != true
+ parentLayoutNode.collapsedSemantics?.isClearingSemantics != true
override fun childHitTest(
layoutNode: LayoutNode,
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
index 655a320..ed5b7c2 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
@@ -18,7 +18,6 @@
package androidx.compose.ui.node
import androidx.annotation.RestrictTo
-import androidx.collection.IntObjectMap
import androidx.compose.runtime.Applier
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.InternalComposeUiApi
@@ -48,7 +47,6 @@
import androidx.compose.ui.platform.TextToolbar
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.platform.WindowInfo
-import androidx.compose.ui.semantics.SemanticsOwner
import androidx.compose.ui.spatial.RectManager
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
@@ -70,9 +68,6 @@
/** The root layout node in the component tree. */
val root: LayoutNode
- /** A mapping of semantic id to LayoutNode. */
- val layoutNodes: IntObjectMap<LayoutNode>
-
/** Draw scope reused for drawing speed up. */
val sharedDrawScope: LayoutNodeDrawScope
@@ -134,8 +129,6 @@
val pointerIconService: PointerIconService
- val semanticsOwner: SemanticsOwner
-
/** Provide a focus owner that controls focus within Compose. */
val focusOwner: FocusOwner
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/SemanticsModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/SemanticsModifierNode.kt
index 002be43..dd3e2f8 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/SemanticsModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/SemanticsModifierNode.kt
@@ -87,14 +87,7 @@
fun SemanticsPropertyReceiver.applySemantics()
}
-/**
- * Invalidate semantics associated with this node. This will reset the [SemanticsConfiguration]
- * associated with the layout node backing this modifier node, and will re-calculate it the next
- * time the [SemanticsConfiguration] is read.
- */
-fun SemanticsModifierNode.invalidateSemantics() {
- requireLayoutNode().invalidateSemantics()
-}
+fun SemanticsModifierNode.invalidateSemantics() = requireLayoutNode().invalidateSemantics()
internal val SemanticsConfiguration.useMinimumTouchTarget: Boolean
get() = getOrNull(SemanticsActions.OnClick) != null
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsInfo.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsInfo.kt
deleted file mode 100644
index 1f2cc5d..0000000
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsInfo.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2024 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.runtime.collection.MutableVector
-import androidx.compose.ui.layout.LayoutInfo
-import androidx.compose.ui.layout.Placeable
-import androidx.compose.ui.node.LayoutNode
-
-/**
- * This is an internal interface that can be used by [SemanticsListener]s to read semantic
- * information from layout nodes. The root [SemanticsInfo] can be accessed using
- * [SemanticsOwner.rootInfo], and particular [SemanticsInfo] can be looked up by their [semanticsId]
- * by using [SemanticsOwner.get].
- */
-internal interface SemanticsInfo : LayoutInfo {
- /** The semantics configuration (Semantic properties and actions) associated with this node. */
- val semanticsConfiguration: SemanticsConfiguration?
-
- /**
- * The [SemanticsInfo] of the parent.
- *
- * This includes parents that do not have any semantics modifiers.
- */
- override val parentInfo: SemanticsInfo?
-
- /**
- * Returns the children list sorted by their [LayoutNode.zIndex] first (smaller first) and the
- * order they were placed via [Placeable.placeAt] by parent (smaller first). Please note that
- * this list contains not placed items as well, so you have to manually filter them.
- *
- * Note that the object is reused so you shouldn't save it for later.
- */
- val childrenInfo: MutableVector<SemanticsInfo>
-}
-
-/** The semantics parent (nearest ancestor which has semantic properties). */
-internal fun SemanticsInfo.findSemanticsParent(): SemanticsInfo? {
- var parent = parentInfo
- while (parent != null) {
- if (parent.semanticsConfiguration != null) return parent
- parent = parent.parentInfo
- }
- return null
-}
-
-/** The nearest semantics ancestor that is merging descendants. */
-internal fun SemanticsInfo.findMergingSemanticsParent(): SemanticsInfo? {
- var parent = parentInfo
- while (parent != null) {
- if (parent.semanticsConfiguration?.isMergingSemanticsOfDescendants == true) return parent
- parent = parent.parentInfo
- }
- return null
-}
-
-internal inline fun SemanticsInfo.findSemanticsChildren(
- includeDeactivated: Boolean = false,
- block: (SemanticsInfo) -> Unit
-) {
- val unvisitedStack = MutableVector<SemanticsInfo>(childrenInfo.size)
- childrenInfo.forEachReversed { unvisitedStack += it }
- while (unvisitedStack.isNotEmpty()) {
- val child = unvisitedStack.removeAt(unvisitedStack.lastIndex)
- when {
- child.isDeactivated && !includeDeactivated -> continue
- child.semanticsConfiguration != null -> block(child)
- else -> child.childrenInfo.forEachReversed { unvisitedStack += it }
- }
- }
-}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsListener.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsListener.kt
deleted file mode 100644
index b51d7c8..0000000
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsListener.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2024 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
-
-/** A listener that can be used to observe semantic changes. */
-internal interface SemanticsListener {
-
- /**
- * [onSemanticsChanged] is called when the [SemanticsConfiguration] of a LayoutNode changes, or
- * when a node calls SemanticsModifierNode.invalidateSemantics.
- *
- * @param semanticsInfo the current [SemanticsInfo] of the layout node that has changed.
- * @param previousSemanticsConfiguration the previous [SemanticsConfiguration] associated with
- * the layout node.
- */
- fun onSemanticsChanged(
- semanticsInfo: SemanticsInfo,
- previousSemanticsConfiguration: SemanticsConfiguration?
- )
-}
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 4bbacd0..35478d6 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
@@ -45,7 +45,7 @@
layoutNode.nodes.head(Nodes.Semantics)!!.node,
mergingEnabled,
layoutNode,
- layoutNode.semanticsConfiguration!!
+ layoutNode.collapsedSemantics!!
)
internal fun SemanticsNode(
@@ -70,7 +70,7 @@
outerSemanticsNode.node,
mergingEnabled,
layoutNode,
- layoutNode.semanticsConfiguration ?: SemanticsConfiguration()
+ layoutNode.collapsedSemantics ?: SemanticsConfiguration()
)
/**
@@ -99,7 +99,7 @@
!isFake &&
replacedChildren.isEmpty() &&
layoutNode.findClosestParentNode {
- it.semanticsConfiguration?.isMergingSemanticsOfDescendants == true
+ it.collapsedSemantics?.isMergingSemanticsOfDescendants == true
} == null
/** The [LayoutInfo] that this is associated with. */
@@ -345,7 +345,7 @@
if (mergingEnabled) {
node =
this.layoutNode.findClosestParentNode {
- it.semanticsConfiguration?.isMergingSemanticsOfDescendants == true
+ it.collapsedSemantics?.isMergingSemanticsOfDescendants == true
}
}
@@ -474,9 +474,7 @@
* Executes [selector] on every parent of this [LayoutNode] and returns the closest [LayoutNode] to
* return `true` from [selector] or null if [selector] returns false for all ancestors.
*/
-internal inline fun LayoutNode.findClosestParentNode(
- selector: (LayoutNode) -> Boolean
-): LayoutNode? {
+internal fun LayoutNode.findClosestParentNode(selector: (LayoutNode) -> Boolean): LayoutNode? {
var currentParent = this.parent
while (currentParent != null) {
if (selector(currentParent)) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsOwner.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsOwner.kt
index b987155..dffed0f 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsOwner.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsOwner.kt
@@ -16,8 +16,6 @@
package androidx.compose.ui.semantics
-import androidx.collection.IntObjectMap
-import androidx.collection.MutableObjectList
import androidx.compose.ui.node.LayoutNode
import androidx.compose.ui.util.fastForEach
@@ -25,8 +23,7 @@
class SemanticsOwner
internal constructor(
private val rootNode: LayoutNode,
- private val outerSemanticsNode: EmptySemanticsModifier,
- private val nodes: IntObjectMap<LayoutNode>
+ private val outerSemanticsNode: EmptySemanticsModifier
) {
/**
* The root node of the semantics tree. Does not contain any unmerged data. May contain merged
@@ -50,22 +47,6 @@
unmergedConfig = SemanticsConfiguration()
)
}
-
- internal val listeners = MutableObjectList<SemanticsListener>(2)
-
- internal val rootInfo: SemanticsInfo
- get() = rootNode
-
- internal operator fun get(semanticsId: Int): SemanticsInfo? {
- return nodes[semanticsId]
- }
-
- internal fun notifySemanticsChange(
- semanticsInfo: SemanticsInfo,
- previousSemanticsConfiguration: SemanticsConfiguration?
- ) {
- listeners.forEach { it.onSemanticsChanged(semanticsInfo, previousSemanticsConfiguration) }
- }
}
/**
@@ -89,7 +70,6 @@
.toList()
}
-@Suppress("unused")
@Deprecated(message = "Use a new overload instead", level = DeprecationLevel.HIDDEN)
fun SemanticsOwner.getAllSemanticsNodes(mergingEnabled: Boolean) =
getAllSemanticsNodes(mergingEnabled, true)
diff --git a/datastore/datastore-preferences/build.gradle b/datastore/datastore-preferences/build.gradle
index 9fb7310..47d0108 100644
--- a/datastore/datastore-preferences/build.gradle
+++ b/datastore/datastore-preferences/build.gradle
@@ -28,12 +28,8 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
}
-android{
- namespace "androidx.datastore.preferences"
-}
androidXMultiplatform {
jvm()
mac()
@@ -41,7 +37,17 @@
ios()
watchos()
tvos()
- android()
+ androidLibrary {
+ namespace = "androidx.datastore.preferences"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ }
defaultPlatform(PlatformIdentifier.ANDROID)
@@ -93,7 +99,6 @@
}
}
-
androidx {
name = "Preferences DataStore"
type = LibraryType.PUBLISHED_LIBRARY
diff --git a/datastore/datastore/build.gradle b/datastore/datastore/build.gradle
index 0933373..80d4bbd6 100644
--- a/datastore/datastore/build.gradle
+++ b/datastore/datastore/build.gradle
@@ -27,12 +27,8 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
}
-android {
- namespace "androidx.datastore.datastore"
-}
androidXMultiplatform {
jvm()
@@ -41,7 +37,17 @@
watchos()
tvos()
linux()
- android()
+ androidLibrary {
+ namespace = "androidx.datastore.datastore"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ }
defaultPlatform(PlatformIdentifier.ANDROID)
diff --git a/development/project-creator/compose-template/groupId/artifactId/build.gradle b/development/project-creator/compose-template/groupId/artifactId/build.gradle
index 9cde1b1..83fd2fb 100644
--- a/development/project-creator/compose-template/groupId/artifactId/build.gradle
+++ b/development/project-creator/compose-template/groupId/artifactId/build.gradle
@@ -28,11 +28,20 @@
plugins {
id("AndroidXPlugin")
id("AndroidXComposePlugin")
- id("com.android.library")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "<PACKAGE>"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ }
jvmStubs()
linuxX64Stubs()
@@ -97,9 +106,6 @@
}
}
-android {
- namespace "<PACKAGE>"
-}
androidx {
name = "<NAME>"
diff --git a/docs/api_guidelines/dependencies.md b/docs/api_guidelines/dependencies.md
index 74b88cd..c6f1576 100644
--- a/docs/api_guidelines/dependencies.md
+++ b/docs/api_guidelines/dependencies.md
@@ -236,15 +236,19 @@
}
```
-### System health {#dependencies-health}
+### Dependency considerations {#dependencies-health}
Generally, Jetpack libraries should avoid dependencies that negatively impact
developers without providing substantial benefit. Libraries should consider the
-system health implications of their dependencies, including:
+implications of their dependencies, including:
- Large dependencies where only a small portion is needed (e.g. APK bloat)
- Dependencies that slow down build times through annotation processing or
compiler overhead
+- Dependencies which do not maintain binary compatibility and conflict with
+ semantic versioning guarantees
+- Dependencies that are intended for server environments and don't interact
+ well with the Android build toolchain (e.g. R8) or runtime (e.g. ART)
#### Kotlin {#dependencies-kotlin}
@@ -281,6 +285,12 @@
}
```
+#### GSON {#dependencies-gson}
+
+GSON relies heavily on reflection and interacts poorly with app optimization
+tools like R8. Instead, consider using `org.json` which is included in the
+Android platform SDK.
+
#### Guava {#dependencies-guava}
The full Guava library is very large and should only be used in cases where
diff --git a/graphics/graphics-shapes/build.gradle b/graphics/graphics-shapes/build.gradle
index 7212f00..000cc74 100644
--- a/graphics/graphics-shapes/build.gradle
+++ b/graphics/graphics-shapes/build.gradle
@@ -26,12 +26,20 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
}
-
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.graphics.shapes"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ }
desktop()
linux()
ios()
@@ -99,9 +107,6 @@
}
}
-android {
- namespace "androidx.graphics.shapes"
-}
androidx {
name = "Graphics Shapes"
diff --git a/libraryversions.toml b/libraryversions.toml
index 5bb21b8..6ee9fb1 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -69,7 +69,7 @@
GRAPHICS_CORE = "1.0.0"
GRAPHICS_FILTERS = "1.0.0-alpha01"
GRAPHICS_PATH = "1.0.0-rc01"
-GRAPHICS_SHAPES = "1.0.0-rc01"
+GRAPHICS_SHAPES = "1.1.0-alpha01"
GRIDLAYOUT = "1.1.0-beta02"
HEALTH_CONNECT = "1.1.0-alpha10"
HEALTH_CONNECT_TESTING_QUARANTINE = "1.0.0-alpha01"
diff --git a/lifecycle/lifecycle-runtime-compose/build.gradle b/lifecycle/lifecycle-runtime-compose/build.gradle
index a802e2f..7a93d89 100644
--- a/lifecycle/lifecycle-runtime-compose/build.gradle
+++ b/lifecycle/lifecycle-runtime-compose/build.gradle
@@ -27,12 +27,31 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
id("AndroidXComposePlugin")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.lifecycle.runtime.compose"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+
+ compileSdk = 35
+ aarMetadata.minCompileSdk = 35
+
+ optimization {
+ it.consumerKeepRules.publish = true
+ it.consumerKeepRules.files.add(
+ new File(project.projectDir, "proguard-rules.pro")
+ )
+ }
+ }
jvmStubs()
linuxX64Stubs()
@@ -94,12 +113,3 @@
samples(project(":lifecycle:lifecycle-runtime-compose:lifecycle-runtime-compose-samples"))
}
-android {
- compileSdk 35
-
- buildTypes.configureEach {
- consumerProguardFiles "proguard-rules.pro"
- }
-
- namespace "androidx.lifecycle.runtime.compose"
-}
diff --git a/lifecycle/lifecycle-runtime-ktx/build.gradle b/lifecycle/lifecycle-runtime-ktx/build.gradle
index e660299..049067d 100644
--- a/lifecycle/lifecycle-runtime-ktx/build.gradle
+++ b/lifecycle/lifecycle-runtime-ktx/build.gradle
@@ -27,11 +27,20 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.lifecycle.ktx"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ }
defaultPlatform(PlatformIdentifier.ANDROID)
@@ -60,6 +69,3 @@
description = "Kotlin extensions for 'lifecycle' artifact"
}
-android {
- namespace "androidx.lifecycle.ktx"
-}
diff --git a/lifecycle/lifecycle-runtime-testing/build.gradle b/lifecycle/lifecycle-runtime-testing/build.gradle
index 430b902..89a61a8 100644
--- a/lifecycle/lifecycle-runtime-testing/build.gradle
+++ b/lifecycle/lifecycle-runtime-testing/build.gradle
@@ -27,11 +27,20 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.lifecycle.testing"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ }
desktop()
mac()
linux()
@@ -73,13 +82,10 @@
androidx {
name = "Lifecycle Runtime Testing"
- type = LibraryType.PUBLISHED_LIBRARY
+ type = LibraryType.PUBLISHED_TEST_LIBRARY
inceptionYear = "2019"
description = "Testing utilities for 'lifecycle' artifact"
legacyDisableKotlinStrictApiMode = true
metalavaK2UastEnabled = false
}
-android {
- namespace "androidx.lifecycle.testing"
-}
diff --git a/lifecycle/lifecycle-viewmodel-testing/build.gradle b/lifecycle/lifecycle-viewmodel-testing/build.gradle
index bd1b11a..919a73e 100644
--- a/lifecycle/lifecycle-viewmodel-testing/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-testing/build.gradle
@@ -29,11 +29,20 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.lifecycle.viewmodel.testing"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ }
desktop()
mac()
linux()
@@ -108,6 +117,3 @@
metalavaK2UastEnabled = false
}
-android {
- namespace "androidx.lifecycle.viewmodel.testing"
-}
diff --git a/navigation/navigation-runtime/api/current.txt b/navigation/navigation-runtime/api/current.txt
index b801964..a4cc6458 100644
--- a/navigation/navigation-runtime/api/current.txt
+++ b/navigation/navigation-runtime/api/current.txt
@@ -113,6 +113,7 @@
method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner(@IdRes int navGraphId);
method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getVisibleEntries();
method @MainThread public boolean handleDeepLink(android.content.Intent? intent);
+ method @MainThread public final boolean handleDeepLink(androidx.navigation.NavDeepLinkRequest request);
method @MainThread public void navigate(android.net.Uri deepLink);
method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions);
method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
diff --git a/navigation/navigation-runtime/api/restricted_current.txt b/navigation/navigation-runtime/api/restricted_current.txt
index b801964..a4cc6458 100644
--- a/navigation/navigation-runtime/api/restricted_current.txt
+++ b/navigation/navigation-runtime/api/restricted_current.txt
@@ -113,6 +113,7 @@
method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner(@IdRes int navGraphId);
method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getVisibleEntries();
method @MainThread public boolean handleDeepLink(android.content.Intent? intent);
+ method @MainThread public final boolean handleDeepLink(androidx.navigation.NavDeepLinkRequest request);
method @MainThread public void navigate(android.net.Uri deepLink);
method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions);
method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
index 1fbe388d..95a9f20 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
@@ -3331,6 +3331,66 @@
@UiThreadTest
@Test
+ fun testHandleDeepLinkAsUri() {
+ val navController = createNavController()
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ navController.setGraph(R.navigation.nav_simple)
+
+ val deepLink = Uri.parse("test-app://test/333")
+ val request = NavDeepLinkRequest.Builder.fromUri(deepLink).build()
+ assertWithMessage("NavController should handle deep links to its own graph")
+ .that(navController.handleDeepLink(request))
+ .isTrue()
+ // Verify that we navigated down to the deep link
+ assertThat(navigator.backStack.map { it.destination.id })
+ .containsExactly(R.id.start_test, R.id.nonNullableArg_test)
+ .inOrder()
+
+ val destination = navController.currentDestination
+ assertThat(destination?.id ?: 0).isEqualTo(R.id.nonNullableArg_test)
+ }
+
+ @UiThreadTest
+ @Test
+ fun testHandleDeepLink_InvalidUri() {
+ val navController = createNavController()
+ navController.setGraph(R.navigation.nav_simple)
+
+ val deepLink = Uri.parse("test-app://invalid/uri")
+ val request = NavDeepLinkRequest.Builder.fromUri(deepLink).build()
+ assertWithMessage("NavController should not match with any deeplink due to invalid uri")
+ .that(navController.handleDeepLink(request))
+ .isFalse()
+ }
+
+ @UiThreadTest
+ @Test
+ fun testHandleDeepLink_MimeType() {
+ val navController = createNavController()
+ navController.setGraph(R.navigation.nav_simple)
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ val deepLink = NavDeepLinkRequest(Uri.parse("invalidDeepLink.com"), null, "type/test")
+
+ navController.handleDeepLink(deepLink)
+ assertThat(navController.currentDestination?.id ?: 0).isEqualTo(R.id.second_test)
+ assertThat(navigator.backStack.size).isEqualTo(2)
+ }
+
+ @UiThreadTest
+ @Test
+ fun testHandleDeepLink_Action() {
+ val navController = createNavController()
+ navController.setGraph(R.navigation.nav_simple)
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ val deepLink = NavDeepLinkRequest(null, "test.action", null)
+
+ navController.handleDeepLink(deepLink)
+ assertThat(navController.currentDestination?.id ?: 0).isEqualTo(R.id.second_test)
+ assertThat(navigator.backStack.size).isEqualTo(2)
+ }
+
+ @UiThreadTest
+ @Test
fun testHandleDeepLinkActionMissingURI_nonNullableArg() {
val navController = createNavController()
navController.setGraph(R.navigation.nav_simple)
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
index ab95516..8efc3e2 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
@@ -1539,7 +1539,55 @@
}
return true
}
- if (flags and Intent.FLAG_ACTIVITY_NEW_TASK != 0) {
+ return handleDeepLink(deepLink, args, flags and Intent.FLAG_ACTIVITY_NEW_TASK != 0)
+ }
+
+ /**
+ * Checks the given NavDeepLinkRequest for a Navigation deep link and navigates to the
+ * destination if present.
+ *
+ * The [navigation graph][graph] should be set before calling this method.
+ *
+ * @param request The request that contains a valid deep link, an action or a mimeType.
+ * @return True if the navigation controller found a valid deep link and navigated to it.
+ * @throws IllegalStateException if deep link cannot be accessed from the current destination
+ * @see NavDestination.addDeepLink
+ */
+ @MainThread
+ public fun handleDeepLink(request: NavDeepLinkRequest): Boolean {
+ val currGraph = backQueue.getTopGraph()
+ val matchingDeepLink =
+ currGraph.matchDeepLinkComprehensive(
+ navDeepLinkRequest = request,
+ searchChildren = true,
+ searchParent = true,
+ lastVisited = currGraph
+ )
+ if (matchingDeepLink != null) {
+ val destination = matchingDeepLink.destination
+ val deepLink = destination.buildDeepLinkIds()
+ val globalArgs = Bundle()
+ val destinationArgs = destination.addInDefaultArgs(matchingDeepLink.matchingArgs)
+ if (destinationArgs != null) {
+ globalArgs.putAll(destinationArgs)
+ }
+ val args = arrayOfNulls<Bundle>(deepLink.size)
+ for (index in args.indices) {
+ val arguments = Bundle()
+ arguments.putAll(globalArgs)
+ args[index] = arguments
+ }
+ return handleDeepLink(deepLink, args, true)
+ }
+ return false
+ }
+
+ private fun handleDeepLink(
+ deepLink: IntArray,
+ args: Array<Bundle?>,
+ newTask: Boolean
+ ): Boolean {
+ if (newTask) {
// Start with a cleared task starting at our root when we're on our own task
if (!backQueue.isEmpty()) {
popBackStackInternal(_graph!!.id, true)
diff --git a/pdf/pdf-viewer/src/main/res/values-af/strings.xml b/pdf/pdf-viewer/src/main/res/values-af/strings.xml
index f62de4d..344991a 100644
--- a/pdf/pdf-viewer/src/main/res/values-af/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-af/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Volgende"</string>
<string name="close_button_description" msgid="7379823906921067675">"Maak toe"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Geen passende resultate nie"</string>
<string name="action_edit" msgid="5882082700509010966">"Wysig lêer"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Voer wagwoord in om te ontsluit"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-am/strings.xml b/pdf/pdf-viewer/src/main/res/values-am/strings.xml
index 9251d8b..7db9b5a 100644
--- a/pdf/pdf-viewer/src/main/res/values-am/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-am/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"ቀጣይ"</string>
<string name="close_button_description" msgid="7379823906921067675">"ዝጋ"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"ምንም የሚመሳሰሉ ውጤቶች የሉም"</string>
<string name="action_edit" msgid="5882082700509010966">"ፋይል አርትዕ"</string>
<string name="password_not_entered" msgid="8875370870743585303">"ለመክፈት የይለፍ ቃል ያስገቡ"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-ar/strings.xml b/pdf/pdf-viewer/src/main/res/values-ar/strings.xml
index 7477408..f88ddb3 100644
--- a/pdf/pdf-viewer/src/main/res/values-ar/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ar/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"التالي"</string>
<string name="close_button_description" msgid="7379823906921067675">"إغلاق"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> من أصل <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> من إجمالي <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"ما مِن نتائج مطابقة"</string>
<string name="action_edit" msgid="5882082700509010966">"تعديل الملف"</string>
<string name="password_not_entered" msgid="8875370870743585303">"يجب إدخال كلمة المرور لفتح القفل"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-as/strings.xml b/pdf/pdf-viewer/src/main/res/values-as/strings.xml
index 575762e..c5fbb0d 100644
--- a/pdf/pdf-viewer/src/main/res/values-as/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-as/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"পৰৱৰ্তী"</string>
<string name="close_button_description" msgid="7379823906921067675">"বন্ধ কৰক"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"কোনো মিল থকা ফলাফল নাই"</string>
<string name="action_edit" msgid="5882082700509010966">"ফাইল সম্পাদনা কৰক"</string>
<string name="password_not_entered" msgid="8875370870743585303">"আনলক কৰিবলৈ পাছৱৰ্ড দিয়ক"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-az/strings.xml b/pdf/pdf-viewer/src/main/res/values-az/strings.xml
index 56f789c..f058531 100644
--- a/pdf/pdf-viewer/src/main/res/values-az/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-az/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Növbəti"</string>
<string name="close_button_description" msgid="7379823906921067675">"Bağlayın"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Uyğun gələn nəticə yoxdur"</string>
<string name="action_edit" msgid="5882082700509010966">"Faylı redaktə edin"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Kiliddən çıxarmaq üçün parol daxil edin"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml b/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml
index 98c7f5b..e43a0da 100644
--- a/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Dalje"</string>
<string name="close_button_description" msgid="7379823906921067675">"Zatvori"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> od <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Nema podudarnih rezultata"</string>
<string name="action_edit" msgid="5882082700509010966">"Izmeni fajl"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Unesite lozinku za otključavanje"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-be/strings.xml b/pdf/pdf-viewer/src/main/res/values-be/strings.xml
index e5246b7..0d565d7 100644
--- a/pdf/pdf-viewer/src/main/res/values-be/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-be/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Далей"</string>
<string name="close_button_description" msgid="7379823906921067675">"Закрыць"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> з <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> з <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Супадзенняў няма"</string>
<string name="action_edit" msgid="5882082700509010966">"Рэдагаваць файл"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Увядзіце пароль для разблакіроўкі"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-bg/strings.xml b/pdf/pdf-viewer/src/main/res/values-bg/strings.xml
index 1963288..3af4c32 100644
--- a/pdf/pdf-viewer/src/main/res/values-bg/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-bg/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Напред"</string>
<string name="close_button_description" msgid="7379823906921067675">"Затваряне"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> от <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Няма съответстващи резултати"</string>
<string name="action_edit" msgid="5882082700509010966">"Редактиране на файла"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Въведете паролата, за да отключите"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-bn/strings.xml b/pdf/pdf-viewer/src/main/res/values-bn/strings.xml
index 7e9b6a8..523615d 100644
--- a/pdf/pdf-viewer/src/main/res/values-bn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-bn/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"পরবর্তী"</string>
<string name="close_button_description" msgid="7379823906921067675">"বন্ধ করুন"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"কোনও ফলাফল মিলছে না"</string>
<string name="action_edit" msgid="5882082700509010966">"ফাইল এডিট করুন"</string>
<string name="password_not_entered" msgid="8875370870743585303">"আনলক করতে পাসওয়ার্ড লিখুন"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-bs/strings.xml b/pdf/pdf-viewer/src/main/res/values-bs/strings.xml
index 6e2be84..58cb55b 100644
--- a/pdf/pdf-viewer/src/main/res/values-bs/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-bs/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Naprijed"</string>
<string name="close_button_description" msgid="7379823906921067675">"Zatvaranje"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> od <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Nema podudarnih rezultata"</string>
<string name="action_edit" msgid="5882082700509010966">"Uredite fajl"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Unesite lozinku da otključate fajl"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-ca/strings.xml b/pdf/pdf-viewer/src/main/res/values-ca/strings.xml
index e5237f2..e006cd2 100644
--- a/pdf/pdf-viewer/src/main/res/values-ca/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ca/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Següent"</string>
<string name="close_button_description" msgid="7379823906921067675">"Tanca"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"No hi ha cap resultat coincident"</string>
<string name="action_edit" msgid="5882082700509010966">"Edita el fitxer"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Introdueix la contrasenya per desbloquejar-lo"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-cs/strings.xml b/pdf/pdf-viewer/src/main/res/values-cs/strings.xml
index bb042a7..93a108c 100644
--- a/pdf/pdf-viewer/src/main/res/values-cs/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-cs/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Další"</string>
<string name="close_button_description" msgid="7379823906921067675">"Zavřít"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> z <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Žádné výsledky neodpovídají"</string>
<string name="action_edit" msgid="5882082700509010966">"Upravit soubor"</string>
<string name="password_not_entered" msgid="8875370870743585303">"K odemknutí zadejte heslo"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-da/strings.xml b/pdf/pdf-viewer/src/main/res/values-da/strings.xml
index 120231b..5a5ac17 100644
--- a/pdf/pdf-viewer/src/main/res/values-da/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-da/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Næste"</string>
<string name="close_button_description" msgid="7379823906921067675">"Luk"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Ingen matchende resultater"</string>
<string name="action_edit" msgid="5882082700509010966">"Rediger fil"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Angiv adgangskode for at låse op"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-de/strings.xml b/pdf/pdf-viewer/src/main/res/values-de/strings.xml
index 8af7f80..15c0a75 100644
--- a/pdf/pdf-viewer/src/main/res/values-de/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-de/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Weiter"</string>
<string name="close_button_description" msgid="7379823906921067675">"Schließen"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Keine passenden Ergebnisse"</string>
<string name="action_edit" msgid="5882082700509010966">"Datei bearbeiten"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Gib zum Entsperren ein Passwort ein"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-el/strings.xml b/pdf/pdf-viewer/src/main/res/values-el/strings.xml
index 7f833eb..83efcf5 100644
--- a/pdf/pdf-viewer/src/main/res/values-el/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-el/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Επόμενο"</string>
<string name="close_button_description" msgid="7379823906921067675">"Κλείσιμο"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> από <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Δεν υπάρχουν αντίστοιχα αποτελέσματα"</string>
<string name="action_edit" msgid="5882082700509010966">"Επεξεργασία αρχείου"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Εισαγάγετε τον κωδικό πρόσβασης για ξεκλείδωμα"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml
index b4f73b8..e3344f7 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Next"</string>
<string name="close_button_description" msgid="7379823906921067675">"Close"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"No matching results"</string>
<string name="action_edit" msgid="5882082700509010966">"Edit file"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Enter password to unlock"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rCA/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rCA/strings.xml
index 74126a3..f9bb660 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rCA/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rCA/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Next"</string>
<string name="close_button_description" msgid="7379823906921067675">"Close"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> of <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"No matching results"</string>
<string name="action_edit" msgid="5882082700509010966">"Edit file"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Enter password to unlock"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml
index b4f73b8..e3344f7 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Next"</string>
<string name="close_button_description" msgid="7379823906921067675">"Close"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"No matching results"</string>
<string name="action_edit" msgid="5882082700509010966">"Edit file"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Enter password to unlock"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml
index b4f73b8..e3344f7 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Next"</string>
<string name="close_button_description" msgid="7379823906921067675">"Close"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"No matching results"</string>
<string name="action_edit" msgid="5882082700509010966">"Edit file"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Enter password to unlock"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml b/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml
index ecf2f42..ac55757a 100644
--- a/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Siguiente"</string>
<string name="close_button_description" msgid="7379823906921067675">"Cerrar"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"No hay resultados que coincidan"</string>
<string name="action_edit" msgid="5882082700509010966">"Editar el archivo"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Ingresa la contraseña para desbloquear"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-es/strings.xml b/pdf/pdf-viewer/src/main/res/values-es/strings.xml
index 48a146f..a8395fb 100644
--- a/pdf/pdf-viewer/src/main/res/values-es/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-es/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Siguiente"</string>
<string name="close_button_description" msgid="7379823906921067675">"Cerrar"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"No hay coincidencias"</string>
<string name="action_edit" msgid="5882082700509010966">"Editar archivo"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Introduce la contraseña para desbloquear"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-et/strings.xml b/pdf/pdf-viewer/src/main/res/values-et/strings.xml
index e23221de..a57f96c 100644
--- a/pdf/pdf-viewer/src/main/res/values-et/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-et/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Järgmine"</string>
<string name="close_button_description" msgid="7379823906921067675">"Sule"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Päringule vastavaid tulemusi pole"</string>
<string name="action_edit" msgid="5882082700509010966">"Faili muutmine"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Avamiseks sisestage parool"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-eu/strings.xml b/pdf/pdf-viewer/src/main/res/values-eu/strings.xml
index c408c2b..e46c3a2 100644
--- a/pdf/pdf-viewer/src/main/res/values-eu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-eu/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Hurrengoa"</string>
<string name="close_button_description" msgid="7379823906921067675">"Itxi"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Ez dago bat datorren emaitzarik"</string>
<string name="action_edit" msgid="5882082700509010966">"Editatu fitxategia"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Idatzi pasahitza desblokeatzeko"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-fa/strings.xml b/pdf/pdf-viewer/src/main/res/values-fa/strings.xml
index e017f32..9fe5557 100644
--- a/pdf/pdf-viewer/src/main/res/values-fa/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fa/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"بعدی"</string>
<string name="close_button_description" msgid="7379823906921067675">"بستن"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"نتیجه منطبقی پیدا نشد"</string>
<string name="action_edit" msgid="5882082700509010966">"ویرایش فایل"</string>
<string name="password_not_entered" msgid="8875370870743585303">"گذرواژه را برای بازگشایی قفل وارد کنید"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-fi/strings.xml b/pdf/pdf-viewer/src/main/res/values-fi/strings.xml
index 9b9eb0a..e9e243ac 100644
--- a/pdf/pdf-viewer/src/main/res/values-fi/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fi/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Seuraava"</string>
<string name="close_button_description" msgid="7379823906921067675">"Sulje"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Ei tuloksia"</string>
<string name="action_edit" msgid="5882082700509010966">"Muokkaa tiedostoa"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Poista lukitus lisäämällä salasana"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml b/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml
index 564ac9f..462595c 100644
--- a/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Suivant"</string>
<string name="close_button_description" msgid="7379823906921067675">"Fermer"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Aucun résultat correspondant"</string>
<string name="action_edit" msgid="5882082700509010966">"Modifier le fichier"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Entrez le mot de passe pour déverrouiller le fichier"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-fr/strings.xml b/pdf/pdf-viewer/src/main/res/values-fr/strings.xml
index f8067f1..7b5333c 100644
--- a/pdf/pdf-viewer/src/main/res/values-fr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fr/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Suivant"</string>
<string name="close_button_description" msgid="7379823906921067675">"Fermer"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> sur <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Aucun résultat"</string>
<string name="action_edit" msgid="5882082700509010966">"Modifier le fichier"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Saisissez le mot de passe pour procéder au déverrouillage"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-gl/strings.xml b/pdf/pdf-viewer/src/main/res/values-gl/strings.xml
index e9996c3..f44eedd 100644
--- a/pdf/pdf-viewer/src/main/res/values-gl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-gl/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Seguinte"</string>
<string name="close_button_description" msgid="7379823906921067675">"Pechar"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Non hai ningún resultado que coincida"</string>
<string name="action_edit" msgid="5882082700509010966">"Editar o ficheiro"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Introduce o contrasinal para desbloquear o ficheiro"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-gu/strings.xml b/pdf/pdf-viewer/src/main/res/values-gu/strings.xml
index a976158..3a6a1fe 100644
--- a/pdf/pdf-viewer/src/main/res/values-gu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-gu/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"આગળ"</string>
<string name="close_button_description" msgid="7379823906921067675">"બંધ કરો"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"કોઈ મેળ ખાતું પરિણામ નથી"</string>
<string name="action_edit" msgid="5882082700509010966">"ફાઇલમાં ફેરફાર કરો"</string>
<string name="password_not_entered" msgid="8875370870743585303">"અનલૉક કરવા માટે પાસવર્ડ દાખલ કરો"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-hi/strings.xml b/pdf/pdf-viewer/src/main/res/values-hi/strings.xml
index 1904222..d7febf7 100644
--- a/pdf/pdf-viewer/src/main/res/values-hi/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hi/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"आगे बढ़ें"</string>
<string name="close_button_description" msgid="7379823906921067675">"बंद करें"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="TOTAL">%2$d</xliff:g> में से <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"कोई मिलता-जुलता नतीजा नहीं मिला"</string>
<string name="action_edit" msgid="5882082700509010966">"फ़ाइल में बदलाव करें"</string>
<string name="password_not_entered" msgid="8875370870743585303">"अनलॉक करने के लिए पासवर्ड डालें"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-hr/strings.xml b/pdf/pdf-viewer/src/main/res/values-hr/strings.xml
index d2be613..69663f2 100644
--- a/pdf/pdf-viewer/src/main/res/values-hr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hr/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Sljedeće"</string>
<string name="close_button_description" msgid="7379823906921067675">"Zatvori"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> od <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Nema podudarnih rezultata"</string>
<string name="action_edit" msgid="5882082700509010966">"Uređivanje datoteke"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Unesite zaporku za otključavanje"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-hu/strings.xml b/pdf/pdf-viewer/src/main/res/values-hu/strings.xml
index 13cefe9..746c393 100644
--- a/pdf/pdf-viewer/src/main/res/values-hu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hu/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Következő"</string>
<string name="close_button_description" msgid="7379823906921067675">"Bezárás"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="TOTAL">%2$d</xliff:g>/<xliff:g id="POSITION">%1$d</xliff:g>."</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="TOTAL">%2$d</xliff:g>/<xliff:g id="POSITION">%1$d</xliff:g>."</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Nincs találat"</string>
<string name="action_edit" msgid="5882082700509010966">"Fájl szerkesztése"</string>
<string name="password_not_entered" msgid="8875370870743585303">"A feloldáshoz írja be a jelszót"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-hy/strings.xml b/pdf/pdf-viewer/src/main/res/values-hy/strings.xml
index 53b5c63..0930a77 100644
--- a/pdf/pdf-viewer/src/main/res/values-hy/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hy/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Հաջորդը"</string>
<string name="close_button_description" msgid="7379823906921067675">"Փակել"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g>՝ <xliff:g id="TOTAL">%2$d</xliff:g>-ից"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Համապատասխանող արդյունքներ չկան"</string>
<string name="action_edit" msgid="5882082700509010966">"Փոփոխել ֆայլը"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Մուտքագրեք գաղտնաբառը՝ ապակողպելու համար"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-in/strings.xml b/pdf/pdf-viewer/src/main/res/values-in/strings.xml
index d1c6170b..3b84829 100644
--- a/pdf/pdf-viewer/src/main/res/values-in/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-in/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Berikutnya"</string>
<string name="close_button_description" msgid="7379823906921067675">"Tutup"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Tidak ada hasil yang cocok"</string>
<string name="action_edit" msgid="5882082700509010966">"Edit file"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Masukkan sandi untuk membuka kunci"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-is/strings.xml b/pdf/pdf-viewer/src/main/res/values-is/strings.xml
index 845c448..84ae8bb 100644
--- a/pdf/pdf-viewer/src/main/res/values-is/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-is/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Næsta"</string>
<string name="close_button_description" msgid="7379823906921067675">"Loka"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Engar samsvarandi niðurstöður fundust"</string>
<string name="action_edit" msgid="5882082700509010966">"Breyta skrá"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Sláðu inn aðgangsorð til að opna"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-it/strings.xml b/pdf/pdf-viewer/src/main/res/values-it/strings.xml
index bea8957..b566885 100644
--- a/pdf/pdf-viewer/src/main/res/values-it/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-it/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Avanti"</string>
<string name="close_button_description" msgid="7379823906921067675">"Chiudi"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Nessun risultato corrispondente"</string>
<string name="action_edit" msgid="5882082700509010966">"Modifica file"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Inserisci la password per sbloccare il file"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-iw/strings.xml b/pdf/pdf-viewer/src/main/res/values-iw/strings.xml
index e7adcaa..08eb4b5 100644
--- a/pdf/pdf-viewer/src/main/res/values-iw/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-iw/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"הבא"</string>
<string name="close_button_description" msgid="7379823906921067675">"סגירה"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> מתוך <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"לא נמצאו תוצאות תואמות"</string>
<string name="action_edit" msgid="5882082700509010966">"עריכת הקובץ"</string>
<string name="password_not_entered" msgid="8875370870743585303">"צריך להזין סיסמה לביטול הנעילה"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-ja/strings.xml b/pdf/pdf-viewer/src/main/res/values-ja/strings.xml
index e2161ea..09692f7 100644
--- a/pdf/pdf-viewer/src/main/res/values-ja/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ja/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"次へ"</string>
<string name="close_button_description" msgid="7379823906921067675">"閉じる"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"一致する結果がありません"</string>
<string name="action_edit" msgid="5882082700509010966">"ファイルを編集"</string>
<string name="password_not_entered" msgid="8875370870743585303">"ロックを解除するには、パスワードを入力してください"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-ka/strings.xml b/pdf/pdf-viewer/src/main/res/values-ka/strings.xml
index 1d76eda..e131512 100644
--- a/pdf/pdf-viewer/src/main/res/values-ka/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ka/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"შემდეგი"</string>
<string name="close_button_description" msgid="7379823906921067675">"დახურვა"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="TOTAL">%2$d</xliff:g>-დან <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"შედეგებში არ არის დამთხვევა"</string>
<string name="action_edit" msgid="5882082700509010966">"ფაილის რედაქტირება"</string>
<string name="password_not_entered" msgid="8875370870743585303">"პაროლის შეყვანა განბლოკვისთვის"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-kk/strings.xml b/pdf/pdf-viewer/src/main/res/values-kk/strings.xml
index 6640153..31e081d 100644
--- a/pdf/pdf-viewer/src/main/res/values-kk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-kk/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Келесі"</string>
<string name="close_button_description" msgid="7379823906921067675">"Жабу"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Сәйкес нәтижелер табылмады."</string>
<string name="action_edit" msgid="5882082700509010966">"Файлды өңдеу"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Құлыпты ашу үшін құпия сөзді енгізіңіз."</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-km/strings.xml b/pdf/pdf-viewer/src/main/res/values-km/strings.xml
index 11cd884..5ce58c9 100644
--- a/pdf/pdf-viewer/src/main/res/values-km/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-km/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"បន្ទាប់"</string>
<string name="close_button_description" msgid="7379823906921067675">"បិទ"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> នៃ <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"គ្មានលទ្ធផលត្រូវគ្នាទេ"</string>
<string name="action_edit" msgid="5882082700509010966">"កែឯកសារ"</string>
<string name="password_not_entered" msgid="8875370870743585303">"បញ្ចូលពាក្យសម្ងាត់ ដើម្បីដោះសោ"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-kn/strings.xml b/pdf/pdf-viewer/src/main/res/values-kn/strings.xml
index 65e39a0..a94de26 100644
--- a/pdf/pdf-viewer/src/main/res/values-kn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-kn/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"ಮುಂದಿನದು"</string>
<string name="close_button_description" msgid="7379823906921067675">"ಮುಚ್ಚಿರಿ"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"ಯಾವುದೇ ಹೊಂದಾಣಿಕೆಯ ಫಲಿತಾಂಶಗಳಿಲ್ಲ"</string>
<string name="action_edit" msgid="5882082700509010966">"ಫೈಲ್ ಎಡಿಟ್ ಮಾಡಿ"</string>
<string name="password_not_entered" msgid="8875370870743585303">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಪಾಸವರ್ಡ್ ಅನ್ನು ನಮೂದಿಸಿ"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-ko/strings.xml b/pdf/pdf-viewer/src/main/res/values-ko/strings.xml
index 0726b77..2dc42f3 100644
--- a/pdf/pdf-viewer/src/main/res/values-ko/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ko/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"다음"</string>
<string name="close_button_description" msgid="7379823906921067675">"닫기"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"일치하는 결과 없음"</string>
<string name="action_edit" msgid="5882082700509010966">"파일 수정"</string>
<string name="password_not_entered" msgid="8875370870743585303">"잠금 해제하려면 비밀번호 입력"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-ky/strings.xml b/pdf/pdf-viewer/src/main/res/values-ky/strings.xml
index 1a2783b..d88a930 100644
--- a/pdf/pdf-viewer/src/main/res/values-ky/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ky/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Кийинки"</string>
<string name="close_button_description" msgid="7379823906921067675">"Жабуу"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Эч нерсе табылган жок"</string>
<string name="action_edit" msgid="5882082700509010966">"Файлды түзөтүү"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Кулпусун ачуу үчүн сырсөздү териңиз"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-lo/strings.xml b/pdf/pdf-viewer/src/main/res/values-lo/strings.xml
index 6eee324..4f4a7379 100644
--- a/pdf/pdf-viewer/src/main/res/values-lo/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-lo/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"ຕໍ່ໄປ"</string>
<string name="close_button_description" msgid="7379823906921067675">"ປິດ"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"ບໍ່ມີຜົນໄດ້ຮັບທີ່ກົງກັນ"</string>
<string name="action_edit" msgid="5882082700509010966">"ແກ້ໄຂໄຟລ໌"</string>
<string name="password_not_entered" msgid="8875370870743585303">"ໃສ່ລະຫັດເພື່ອປົດລັອກ"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-lt/strings.xml b/pdf/pdf-viewer/src/main/res/values-lt/strings.xml
index 44ef381..aea04bf 100644
--- a/pdf/pdf-viewer/src/main/res/values-lt/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-lt/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Kitas"</string>
<string name="close_button_description" msgid="7379823906921067675">"Uždaryti"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> iš <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Atitikusių rezultatų nerasta"</string>
<string name="action_edit" msgid="5882082700509010966">"Redaguoti failą"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Įveskite slaptažodį, kad atrakintumėte"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-lv/strings.xml b/pdf/pdf-viewer/src/main/res/values-lv/strings.xml
index 62b43d2..8e489b8 100644
--- a/pdf/pdf-viewer/src/main/res/values-lv/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-lv/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Tālāk"</string>
<string name="close_button_description" msgid="7379823906921067675">"Aizvērt"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"numur <xliff:g id="POSITION">%1$d</xliff:g>, kopējais skaits ir <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Nav atbilstošu rezultātu."</string>
<string name="action_edit" msgid="5882082700509010966">"Rediģēt failu"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Lai atbloķētu, ievadiet paroli."</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-mk/strings.xml b/pdf/pdf-viewer/src/main/res/values-mk/strings.xml
index 86424ba..f7b0a43 100644
--- a/pdf/pdf-viewer/src/main/res/values-mk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-mk/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Следно"</string>
<string name="close_button_description" msgid="7379823906921067675">"Затвори"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Нема резултати што се совпаѓаат"</string>
<string name="action_edit" msgid="5882082700509010966">"Изменете ја датотеката"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Внесете лозинка за да отклучите"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-ml/strings.xml b/pdf/pdf-viewer/src/main/res/values-ml/strings.xml
index e1c7e40..18dda28 100644
--- a/pdf/pdf-viewer/src/main/res/values-ml/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ml/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"അടുത്തത്"</string>
<string name="close_button_description" msgid="7379823906921067675">"അടയ്ക്കുക"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="TOTAL">%2$d</xliff:g>-ൽ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"പൊരുത്തപ്പെടുന്ന ഫലങ്ങളൊന്നുമില്ല"</string>
<string name="action_edit" msgid="5882082700509010966">"ഫയൽ എഡിറ്റ് ചെയ്യുക"</string>
<string name="password_not_entered" msgid="8875370870743585303">"അൺലോക്ക് ചെയ്യാൻ പാസ്വേഡ് നൽകുക"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-mn/strings.xml b/pdf/pdf-viewer/src/main/res/values-mn/strings.xml
index eee4b83..5a406cd 100644
--- a/pdf/pdf-viewer/src/main/res/values-mn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-mn/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Дараах"</string>
<string name="close_button_description" msgid="7379823906921067675">"Хаах"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Ямар ч тохирох илэрц байхгүй"</string>
<string name="action_edit" msgid="5882082700509010966">"Файлыг засах"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Түгжээг тайлахын тулд нууц үг оруулна уу"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-mr/strings.xml b/pdf/pdf-viewer/src/main/res/values-mr/strings.xml
index 73892fc..663888f 100644
--- a/pdf/pdf-viewer/src/main/res/values-mr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-mr/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"पुढील"</string>
<string name="close_button_description" msgid="7379823906921067675">"बंद करा"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="TOTAL">%2$d</xliff:g> पैकी <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"कोणतेही जुळणारे परिणाम नाहीत"</string>
<string name="action_edit" msgid="5882082700509010966">"फाइल संपादित करा"</string>
<string name="password_not_entered" msgid="8875370870743585303">"अनलॉक करण्यासाठी पासवर्ड एंटर करा"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-ms/strings.xml b/pdf/pdf-viewer/src/main/res/values-ms/strings.xml
index 7d642d7..c4565f0 100644
--- a/pdf/pdf-viewer/src/main/res/values-ms/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ms/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Seterusnya"</string>
<string name="close_button_description" msgid="7379823906921067675">"Tutup"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> daripada <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Tiada hasil carian yang sepadan"</string>
<string name="action_edit" msgid="5882082700509010966">"Edit fail"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Masukkan kata laluan untuk membuka kunci"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-my/strings.xml b/pdf/pdf-viewer/src/main/res/values-my/strings.xml
index e642765..09ff37e 100644
--- a/pdf/pdf-viewer/src/main/res/values-my/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-my/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"ရှေ့သို့"</string>
<string name="close_button_description" msgid="7379823906921067675">"ပိတ်ရန်"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"ကိုက်ညီသောရလဒ် မရှိပါ"</string>
<string name="action_edit" msgid="5882082700509010966">"ဖိုင် တည်းဖြတ်ရန်"</string>
<string name="password_not_entered" msgid="8875370870743585303">"ဖွင့်ရန် စကားဝှက်ထည့်ပါ"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-nb/strings.xml b/pdf/pdf-viewer/src/main/res/values-nb/strings.xml
index 163e356..cb39104 100644
--- a/pdf/pdf-viewer/src/main/res/values-nb/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-nb/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Neste"</string>
<string name="close_button_description" msgid="7379823906921067675">"Lukk"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Ingen treff"</string>
<string name="action_edit" msgid="5882082700509010966">"Endre filen"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Skriv inn passordet for å låse opp"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-ne/strings.xml b/pdf/pdf-viewer/src/main/res/values-ne/strings.xml
index 4530550..5ef47e9 100644
--- a/pdf/pdf-viewer/src/main/res/values-ne/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ne/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"अर्को"</string>
<string name="close_button_description" msgid="7379823906921067675">"बन्द गर्नुहोस्"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"कुनै पनि मिल्दोजुल्दो परिणाम भेटिएन"</string>
<string name="action_edit" msgid="5882082700509010966">"फाइल सम्पादन गर्नुहोस्"</string>
<string name="password_not_entered" msgid="8875370870743585303">"अनलक गर्न पासवर्ड हाल्नुहोस्"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-nl/strings.xml b/pdf/pdf-viewer/src/main/res/values-nl/strings.xml
index 1ddbf68..25fe496 100644
--- a/pdf/pdf-viewer/src/main/res/values-nl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-nl/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Volgende"</string>
<string name="close_button_description" msgid="7379823906921067675">"Sluiten"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> van <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Geen overeenkomende resultaten"</string>
<string name="action_edit" msgid="5882082700509010966">"Bestand bewerken"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Voer het wachtwoord in om te ontgrendelen"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-or/strings.xml b/pdf/pdf-viewer/src/main/res/values-or/strings.xml
index 4b08b7b..e67a2f5 100644
--- a/pdf/pdf-viewer/src/main/res/values-or/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-or/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"ପରବର୍ତ୍ତୀ"</string>
<string name="close_button_description" msgid="7379823906921067675">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="TOTAL">%2$d</xliff:g>ର <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"ମେଳ ହେଉଥିବା କୌଣସି ଫଳାଫଳ ନାହିଁ"</string>
<string name="action_edit" msgid="5882082700509010966">"ଫାଇଲକୁ ଏଡିଟ କରନ୍ତୁ"</string>
<string name="password_not_entered" msgid="8875370870743585303">"ଅନଲକ କରିବା ପାଇଁ ପାସୱାର୍ଡ ଲେଖନ୍ତୁ"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-pa/strings.xml b/pdf/pdf-viewer/src/main/res/values-pa/strings.xml
index e68fbfa..b4845ba 100644
--- a/pdf/pdf-viewer/src/main/res/values-pa/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pa/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"ਅੱਗੇ"</string>
<string name="close_button_description" msgid="7379823906921067675">"ਬੰਦ ਕਰੋ"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"ਕੋਈ ਮੇਲ ਖਾਂਦਾ ਨਤੀਜਾ ਨਹੀਂ"</string>
<string name="action_edit" msgid="5882082700509010966">"ਫ਼ਾਈਲ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
<string name="password_not_entered" msgid="8875370870743585303">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਪਾਸਵਰਡ ਦਾਖਲ ਕਰੋ"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-pl/strings.xml b/pdf/pdf-viewer/src/main/res/values-pl/strings.xml
index 3d52bbc..848f550 100644
--- a/pdf/pdf-viewer/src/main/res/values-pl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pl/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Dalej"</string>
<string name="close_button_description" msgid="7379823906921067675">"Zamknij"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> z <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Brak pasujących wyników"</string>
<string name="action_edit" msgid="5882082700509010966">"Edytuj plik"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Podaj hasło, aby odblokować"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml b/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml
index 57583b90..642730e 100644
--- a/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Próxima"</string>
<string name="close_button_description" msgid="7379823906921067675">"Fechar"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Nenhum resultado encontrado"</string>
<string name="action_edit" msgid="5882082700509010966">"Editar arquivo"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Digite a senha para desbloquear"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml b/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml
index 17901b0..667143f 100644
--- a/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Seguinte"</string>
<string name="close_button_description" msgid="7379823906921067675">"Fechar"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Sem resultados correspondentes"</string>
<string name="action_edit" msgid="5882082700509010966">"Editar ficheiro"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Introduza a palavra-passe para desbloquear"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-pt/strings.xml b/pdf/pdf-viewer/src/main/res/values-pt/strings.xml
index 57583b90..642730e 100644
--- a/pdf/pdf-viewer/src/main/res/values-pt/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pt/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Próxima"</string>
<string name="close_button_description" msgid="7379823906921067675">"Fechar"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Nenhum resultado encontrado"</string>
<string name="action_edit" msgid="5882082700509010966">"Editar arquivo"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Digite a senha para desbloquear"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-ro/strings.xml b/pdf/pdf-viewer/src/main/res/values-ro/strings.xml
index f33ce79..ea88bec 100644
--- a/pdf/pdf-viewer/src/main/res/values-ro/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ro/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Înainte"</string>
<string name="close_button_description" msgid="7379823906921067675">"Închide"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Niciun rezultat"</string>
<string name="action_edit" msgid="5882082700509010966">"Editează fișierul"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Introdu parola pentru a debloca"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-ru/strings.xml b/pdf/pdf-viewer/src/main/res/values-ru/strings.xml
index b3bed54..d062234 100644
--- a/pdf/pdf-viewer/src/main/res/values-ru/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ru/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Далее"</string>
<string name="close_button_description" msgid="7379823906921067675">"Закрыть"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> из <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Ничего не найдено."</string>
<string name="action_edit" msgid="5882082700509010966">"Редактировать файл"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Введите пароль для разблокировки."</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-si/strings.xml b/pdf/pdf-viewer/src/main/res/values-si/strings.xml
index 7aeb8c8..3c22ba7 100644
--- a/pdf/pdf-viewer/src/main/res/values-si/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-si/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"මීළඟ"</string>
<string name="close_button_description" msgid="7379823906921067675">"වසන්න"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"ගැළපෙන ප්රතිඵල නැත"</string>
<string name="action_edit" msgid="5882082700509010966">"ගොනුව සංස්කරණ කරන්න"</string>
<string name="password_not_entered" msgid="8875370870743585303">"අගුලු හැරීමට මුරපදය ඇතුළත් කරන්න"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-sk/strings.xml b/pdf/pdf-viewer/src/main/res/values-sk/strings.xml
index 2c4e7b4..8fdcc4c 100644
--- a/pdf/pdf-viewer/src/main/res/values-sk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sk/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Ďalej"</string>
<string name="close_button_description" msgid="7379823906921067675">"Zavrieť"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Žiadne zodpovedajúce výsledky"</string>
<string name="action_edit" msgid="5882082700509010966">"Upraviť súbor"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Zadajte heslo na odomknutie"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-sl/strings.xml b/pdf/pdf-viewer/src/main/res/values-sl/strings.xml
index 5ff5caa..e43931d 100644
--- a/pdf/pdf-viewer/src/main/res/values-sl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sl/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Naprej"</string>
<string name="close_button_description" msgid="7379823906921067675">"Zapri"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> od <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Ni ustreznih rezultatov"</string>
<string name="action_edit" msgid="5882082700509010966">"Urejanje datoteke"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Vnesite geslo za odklepanje"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-sq/strings.xml b/pdf/pdf-viewer/src/main/res/values-sq/strings.xml
index a78b08a..482261d 100644
--- a/pdf/pdf-viewer/src/main/res/values-sq/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sq/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Para"</string>
<string name="close_button_description" msgid="7379823906921067675">"Mbyll"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Nuk përputhet asnjë rezultat"</string>
<string name="action_edit" msgid="5882082700509010966">"Modifiko skedarin"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Fut fjalëkalimin për ta shkyçur"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-sr/strings.xml b/pdf/pdf-viewer/src/main/res/values-sr/strings.xml
index 61074fd..ccd521a 100644
--- a/pdf/pdf-viewer/src/main/res/values-sr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sr/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Даље"</string>
<string name="close_button_description" msgid="7379823906921067675">"Затвори"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> од <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Нема подударних резултата"</string>
<string name="action_edit" msgid="5882082700509010966">"Измени фајл"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Унесите лозинку за откључавање"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-sv/strings.xml b/pdf/pdf-viewer/src/main/res/values-sv/strings.xml
index e756be8..edbbf58 100644
--- a/pdf/pdf-viewer/src/main/res/values-sv/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sv/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Nästa"</string>
<string name="close_button_description" msgid="7379823906921067675">"Stäng"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Det finns inga matchande resultat"</string>
<string name="action_edit" msgid="5882082700509010966">"Redigera fil"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Ange lösenord för att låsa upp"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-sw/strings.xml b/pdf/pdf-viewer/src/main/res/values-sw/strings.xml
index 9f50a92a..f26bdf5 100644
--- a/pdf/pdf-viewer/src/main/res/values-sw/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sw/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Endelea"</string>
<string name="close_button_description" msgid="7379823906921067675">"Funga"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> kati ya <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Hakuna matokeo yanayolingana"</string>
<string name="action_edit" msgid="5882082700509010966">"Badilisha faili"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Weka nenosiri ili ufungue"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-ta/strings.xml b/pdf/pdf-viewer/src/main/res/values-ta/strings.xml
index 3c329ca..56321e0 100644
--- a/pdf/pdf-viewer/src/main/res/values-ta/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ta/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"அடுத்ததற்குச் செல்லும்"</string>
<string name="close_button_description" msgid="7379823906921067675">"மூடும்"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"பொருந்தும் முடிவுகள் எதுவுமில்லை"</string>
<string name="action_edit" msgid="5882082700509010966">"ஃபைலைத் திருத்து"</string>
<string name="password_not_entered" msgid="8875370870743585303">"அன்லாக் செய்ய கடவுச்சொல்லை டைப் செய்யவும்"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-te/strings.xml b/pdf/pdf-viewer/src/main/res/values-te/strings.xml
index 1b0c0d8..3cc15c5 100644
--- a/pdf/pdf-viewer/src/main/res/values-te/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-te/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"తర్వాత"</string>
<string name="close_button_description" msgid="7379823906921067675">"మూసివేయండి"</string>
<string name="message_match_status" msgid="6288242289981639727">"మొత్తం <xliff:g id="TOTAL">%2$d</xliff:g>లో <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="TOTAL">%2$d</xliff:g>లో <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"మ్యాచ్ అయ్యే ఫలితాలు ఏవీ లేవు"</string>
<string name="action_edit" msgid="5882082700509010966">"ఫైల్ను ఎడిట్ చేయండి"</string>
<string name="password_not_entered" msgid="8875370870743585303">"అన్లాక్ చేయడానికి పాస్వర్డ్ను నమోదు చేయండి"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-th/strings.xml b/pdf/pdf-viewer/src/main/res/values-th/strings.xml
index 17236b8..3f4cacb 100644
--- a/pdf/pdf-viewer/src/main/res/values-th/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-th/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"ถัดไป"</string>
<string name="close_button_description" msgid="7379823906921067675">"ปิด"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> จาก <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"ไม่มีผลลัพธ์ที่ตรงกัน"</string>
<string name="action_edit" msgid="5882082700509010966">"แก้ไขไฟล์"</string>
<string name="password_not_entered" msgid="8875370870743585303">"ป้อนรหัสผ่านเพื่อปลดล็อก"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-tl/strings.xml b/pdf/pdf-viewer/src/main/res/values-tl/strings.xml
index 1daaa55..8dded5f 100644
--- a/pdf/pdf-viewer/src/main/res/values-tl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-tl/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"Susunod"</string>
<string name="close_button_description" msgid="7379823906921067675">"Isara"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> sa <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"Walang tumutugmang resulta"</string>
<string name="action_edit" msgid="5882082700509010966">"I-edit ang file"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Ilagay ang password para i-unlock"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-tr/strings.xml b/pdf/pdf-viewer/src/main/res/values-tr/strings.xml
index c378486..d247153 100644
--- a/pdf/pdf-viewer/src/main/res/values-tr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-tr/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Sonraki"</string>
<string name="close_button_description" msgid="7379823906921067675">"Kapat"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Eşleşen sonuç yok"</string>
<string name="action_edit" msgid="5882082700509010966">"Dosyayı düzenle"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Kilidi açmak için şifreyi girin"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-uk/strings.xml b/pdf/pdf-viewer/src/main/res/values-uk/strings.xml
index 358244d..11c4ecf 100644
--- a/pdf/pdf-viewer/src/main/res/values-uk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-uk/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Далі"</string>
<string name="close_button_description" msgid="7379823906921067675">"Закрити"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Немає результатів"</string>
<string name="action_edit" msgid="5882082700509010966">"Редагувати файл"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Введіть пароль, щоб розблокувати"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-ur/strings.xml b/pdf/pdf-viewer/src/main/res/values-ur/strings.xml
index 97c7973..12ccee6 100644
--- a/pdf/pdf-viewer/src/main/res/values-ur/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ur/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"اگلا"</string>
<string name="close_button_description" msgid="7379823906921067675">"بند کریں"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="TOTAL">%2$d</xliff:g> / <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"<xliff:g id="POSITION">%1$d</xliff:g> از <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"کوئی مماثل نتائج نہیں ہیں"</string>
<string name="action_edit" msgid="5882082700509010966">"فائل میں ترمیم کریں"</string>
<string name="password_not_entered" msgid="8875370870743585303">"غیر مقفل کرنے کیلئے پاس ورڈ درج کریں"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-uz/strings.xml b/pdf/pdf-viewer/src/main/res/values-uz/strings.xml
index eaea88b..82dd413 100644
--- a/pdf/pdf-viewer/src/main/res/values-uz/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-uz/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Keyingisi"</string>
<string name="close_button_description" msgid="7379823906921067675">"Yopish"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Mos keladigani topilmadi"</string>
<string name="action_edit" msgid="5882082700509010966">"Faylni tahrirlash"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Ochish uchun parolni kiriting"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-vi/strings.xml b/pdf/pdf-viewer/src/main/res/values-vi/strings.xml
index 1960c80..42e04e9 100644
--- a/pdf/pdf-viewer/src/main/res/values-vi/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-vi/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Tiếp theo"</string>
<string name="close_button_description" msgid="7379823906921067675">"Đóng"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Không có kết quả phù hợp"</string>
<string name="action_edit" msgid="5882082700509010966">"Chỉnh sửa tệp"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Nhập mật khẩu để mở khoá"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml b/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml
index cf29a52..343768c 100644
--- a/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml
@@ -47,6 +47,7 @@
<string name="next_button_description" msgid="4702699322249103693">"下一页"</string>
<string name="close_button_description" msgid="7379823906921067675">"关闭"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="match_status_description" msgid="4996847358326345288">"第 <xliff:g id="POSITION">%1$d</xliff:g> 个(共 <xliff:g id="TOTAL">%2$d</xliff:g> 个)"</string>
<string name="message_no_match_status" msgid="5929387004361286433">"没有符合条件的结果"</string>
<string name="action_edit" msgid="5882082700509010966">"编辑文件"</string>
<string name="password_not_entered" msgid="8875370870743585303">"请输入密码进行解锁"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml b/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml
index 06c8e92..984fbe1 100644
--- a/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"下一個"</string>
<string name="close_button_description" msgid="7379823906921067675">"閂"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"找不到相符的結果"</string>
<string name="action_edit" msgid="5882082700509010966">"編輯檔案"</string>
<string name="password_not_entered" msgid="8875370870743585303">"輸入密碼即可解鎖"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml b/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml
index 12b583f..915cd4a 100644
--- a/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"下一個"</string>
<string name="close_button_description" msgid="7379823906921067675">"關閉"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"找不到相符的結果"</string>
<string name="action_edit" msgid="5882082700509010966">"編輯檔案"</string>
<string name="password_not_entered" msgid="8875370870743585303">"輸入密碼即可解鎖"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-zu/strings.xml b/pdf/pdf-viewer/src/main/res/values-zu/strings.xml
index a3ad158..23f95f7 100644
--- a/pdf/pdf-viewer/src/main/res/values-zu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zu/strings.xml
@@ -47,6 +47,8 @@
<string name="next_button_description" msgid="4702699322249103693">"Okulandelayo"</string>
<string name="close_button_description" msgid="7379823906921067675">"Vala"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <!-- no translation found for match_status_description (4996847358326345288) -->
+ <skip />
<string name="message_no_match_status" msgid="5929387004361286433">"Ayikho imiphumela efanayo"</string>
<string name="action_edit" msgid="5882082700509010966">"Hlela ifayela"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Faka iphasiwedi ukuvula"</string>
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/ext/xelement_ext.kt b/room/room-compiler/src/main/kotlin/androidx/room/ext/xelement_ext.kt
index bf4537a..28f9f0c 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/ext/xelement_ext.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/ext/xelement_ext.kt
@@ -16,10 +16,8 @@
package androidx.room.ext
-import androidx.room.compiler.processing.XConstructorElement
import androidx.room.compiler.processing.XElement
import androidx.room.compiler.processing.XExecutableParameterElement
-import androidx.room.compiler.processing.XFieldElement
import androidx.room.compiler.processing.XTypeElement
import kotlin.contracts.contract
@@ -28,7 +26,7 @@
return this.hasAnnotation(androidx.room.Entity::class)
}
-fun XTypeElement.getValueClassUnderlyingInfo(): ValueClassInfo {
+fun XTypeElement.getValueClassUnderlyingElement(): XExecutableParameterElement {
check(this.isValueClass()) {
"Can't get value class property, type element '$this' is not a value class"
}
@@ -36,21 +34,12 @@
// * Primary constructor is required for value class
// * Value class must have exactly one primary constructor parameter
// * Value class primary constructor must only have final read-only (val) property parameter
- val constructor =
- checkNotNull(this.findPrimaryConstructor()) {
+ return checkNotNull(this.findPrimaryConstructor()) {
"Couldn't find primary constructor for value class."
}
- val param = constructor.parameters.first()
- val field = getDeclaredFields().first { it.name == param.name }
- return ValueClassInfo(constructor, param, field)
+ .parameters
+ .single()
}
-/** Store information about the underlying value property of a Kotlin value class */
-class ValueClassInfo(
- val constructor: XConstructorElement,
- val parameter: XExecutableParameterElement,
- val field: XFieldElement,
-)
-
/** Suffix of the Kotlin synthetic class created interface method implementations. */
const val DEFAULT_IMPLS_CLASS_NAME = "DefaultImpls"
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
index 86c5e95..798bfeb 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
@@ -28,7 +28,7 @@
import androidx.room.ext.CollectionTypeNames.LONG_SPARSE_ARRAY
import androidx.room.ext.CommonTypeNames
import androidx.room.ext.GuavaTypeNames
-import androidx.room.ext.getValueClassUnderlyingInfo
+import androidx.room.ext.getValueClassUnderlyingElement
import androidx.room.ext.isByteBuffer
import androidx.room.ext.isEntityElement
import androidx.room.ext.isNotByte
@@ -377,15 +377,12 @@
val typeElement = type.typeElement
if (typeElement?.isValueClass() == true) {
// Extract the type value of the Value class element
- val underlyingInfo = typeElement.getValueClassUnderlyingInfo()
- if (underlyingInfo.constructor.isPrivate() || underlyingInfo.field.getter == null) {
- return null
- }
+ val underlyingProperty = typeElement.getValueClassUnderlyingElement()
val underlyingTypeColumnAdapter =
findColumnTypeAdapter(
// Find an adapter for the non-null underlying type, nullability will be handled
// by the value class adapter.
- out = underlyingInfo.parameter.asMemberOf(type).makeNonNullable(),
+ out = underlyingProperty.asMemberOf(type).makeNonNullable(),
affinity = affinity,
skipDefaultConverter = false
) ?: return null
@@ -394,7 +391,7 @@
valueTypeColumnAdapter = underlyingTypeColumnAdapter,
affinity = underlyingTypeColumnAdapter.typeAffinity,
out = type,
- valuePropertyName = underlyingInfo.parameter.name
+ valuePropertyName = underlyingProperty.name
)
}
return when {
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/ext/ElementExtTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/ext/ElementExtTest.kt
index db835d4..1e314bf 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/ext/ElementExtTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/ext/ElementExtTest.kt
@@ -24,7 +24,7 @@
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.XTestInvocation
import androidx.room.compiler.processing.util.compileFiles
-import androidx.room.compiler.processing.util.runKspTest
+import androidx.room.runKspTestWithK1
import androidx.room.runProcessorTestWithK1
import org.junit.Test
import org.junit.runner.RunWith
@@ -225,44 +225,25 @@
"""
package foo
class Subject {
- fun uLongFunction(): ULong = TODO()
- fun durationFunction(): kotlin.time.Duration = TODO()
+ fun makeULong(): ULong {
+ TODO()
+ }
}
"""
.trimIndent()
)
- runKspTest(
+ runKspTestWithK1(
sources = listOf(src),
config =
XProcessingEnvConfig.DEFAULT.copy(excludeMethodsWithInvalidJvmSourceNames = false)
) { invocation ->
val subject = invocation.processingEnv.requireTypeElement("foo.Subject")
- subject
- .getDeclaredMethods()
- .first { it.name == "uLongFunction" }
- .let { uLongFunction ->
- val returnType = uLongFunction.returnType
- val info = checkNotNull(returnType.typeElement).getValueClassUnderlyingInfo()
- assertThat(info.parameter.name).isEqualTo("data")
- assertThat(info.field.name).isEqualTo("data")
- assertThat(info.parameter.type)
- .isEqualTo(invocation.processingEnv.requireType(XTypeName.PRIMITIVE_LONG))
- assertThat(info.field.type)
- .isEqualTo(invocation.processingEnv.requireType(XTypeName.PRIMITIVE_LONG))
- }
- subject
- .getDeclaredMethods()
- .first { it.name == "durationFunction" }
- .let { durationFunction ->
- val returnType = durationFunction.returnType
- val info = checkNotNull(returnType.typeElement).getValueClassUnderlyingInfo()
- assertThat(info.parameter.name).isEqualTo("rawValue")
- assertThat(info.field.name).isEqualTo("rawValue")
- assertThat(info.parameter.type)
- .isEqualTo(invocation.processingEnv.requireType(XTypeName.PRIMITIVE_LONG))
- assertThat(info.field.type)
- .isEqualTo(invocation.processingEnv.requireType(XTypeName.PRIMITIVE_LONG))
- }
+ val returnType =
+ subject.getDeclaredMethods().single { it.name == "makeULong" }.returnType
+ val prop = checkNotNull(returnType.typeElement).getValueClassUnderlyingElement()
+ assertThat(prop.name).isEqualTo("data")
+ assertThat(prop.type)
+ .isEqualTo(invocation.processingEnv.requireType(XTypeName.PRIMITIVE_LONG))
}
}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
index d43a813..29cbd79 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
@@ -30,7 +30,6 @@
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.XTestInvocation
import androidx.room.compiler.processing.util.compileFiles
-import androidx.room.compiler.processing.util.runKspTest
import androidx.room.compiler.processing.util.runProcessorTest
import androidx.room.ext.CommonTypeNames
import androidx.room.ext.GuavaUtilConcurrentTypeNames
@@ -196,6 +195,7 @@
Source.java(
"foo.bar.Fruit",
""" package foo.bar;
+ import androidx.room.*;
enum Fruit {
APPLE,
BANANA,
@@ -222,6 +222,7 @@
Source.kotlin(
"Foo.kt",
"""
+ import androidx.room.*
@JvmInline
value class IntValueClass(val data: Int)
@JvmInline
@@ -281,6 +282,7 @@
Source.kotlin(
"Foo.kt",
"""
+ import androidx.room.*
@JvmInline
value class Foo(val value : Int) {
val double
@@ -290,78 +292,15 @@
.trimIndent()
)
- runKspTest(sources = listOf(source)) { invocation ->
- val store =
- TypeAdapterStore.create(
- context = invocation.context,
- builtInConverterFlags = BuiltInConverterFlags.DEFAULT
- )
- val typeElement = invocation.processingEnv.requireTypeElement("Foo")
- val result =
- store.findColumnTypeAdapter(
- out = typeElement.type,
- affinity = null,
- skipDefaultConverter = false
- )
- assertThat(result).isInstanceOf<ValueClassConverterWrapper>()
- }
- }
-
- @Test
- fun testValueClassWithPrivateVal() {
- val source =
- Source.kotlin(
- "Foo.kt",
- """
- @JvmInline
- value class Foo(private val value : Int)
- """
- .trimIndent()
+ runProcessorTestWithK1(sources = listOf(source)) { invocation ->
+ TypeAdapterStore.create(
+ context = invocation.context,
+ builtInConverterFlags = BuiltInConverterFlags.DEFAULT
)
-
- runKspTest(sources = listOf(source)) { invocation ->
- val store =
- TypeAdapterStore.create(
- context = invocation.context,
- builtInConverterFlags = BuiltInConverterFlags.DEFAULT
- )
val typeElement = invocation.processingEnv.requireTypeElement("Foo")
- val result =
- store.findColumnTypeAdapter(
- out = typeElement.type,
- affinity = null,
- skipDefaultConverter = false
- )
- assertThat(result).isNull()
- }
- }
-
- @Test
- fun testValueClassWithPrivateConstructor() {
- val source =
- Source.kotlin(
- "Foo.kt",
- """
- @JvmInline
- value class Foo private constructor(val value : Int)
- """
- .trimIndent()
- )
-
- runKspTest(sources = listOf(source)) { invocation ->
- val store =
- TypeAdapterStore.create(
- context = invocation.context,
- builtInConverterFlags = BuiltInConverterFlags.DEFAULT
- )
- val typeElement = invocation.processingEnv.requireTypeElement("Foo")
- val result =
- store.findColumnTypeAdapter(
- out = typeElement.type,
- affinity = null,
- skipDefaultConverter = false
- )
- assertThat(result).isNull()
+ assertThat(typeElement.getDeclaredFields()).hasSize(1)
+ assertThat(typeElement.getDeclaredFields().single().type.asTypeName())
+ .isEqualTo(PRIMITIVE_INT)
}
}
diff --git a/room/room-testing/build.gradle b/room/room-testing/build.gradle
index 14a4823..4678633 100644
--- a/room/room-testing/build.gradle
+++ b/room/room-testing/build.gradle
@@ -22,7 +22,6 @@
* modifying its settings.
*/
-
import androidx.build.KotlinTarget
import androidx.build.PlatformIdentifier
import androidx.build.LibraryType
@@ -30,11 +29,20 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.room.testing"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ }
ios()
jvm()
linux()
@@ -88,9 +96,6 @@
}
}
-android {
- namespace "androidx.room.testing"
-}
androidx {
name = "Room Testing"
diff --git a/savedstate/savedstate/api/current.txt b/savedstate/savedstate/api/current.txt
index fe1c8f0..64e0148 100644
--- a/savedstate/savedstate/api/current.txt
+++ b/savedstate/savedstate/api/current.txt
@@ -13,8 +13,11 @@
@kotlin.jvm.JvmInline public final value class SavedStateReader {
ctor public SavedStateReader(android.os.Bundle source);
method public inline operator boolean contains(String key);
+ method public boolean contentDeepEquals(android.os.Bundle other);
method public inline boolean getBoolean(String key);
method public inline boolean getBooleanOrElse(String key, kotlin.jvm.functions.Function0<java.lang.Boolean> defaultValue);
+ method public inline char getChar(String key);
+ method public inline char getCharOrElse(String key, kotlin.jvm.functions.Function0<java.lang.Character> defaultValue);
method public inline double getDouble(String key);
method public inline double getDoubleOrElse(String key, kotlin.jvm.functions.Function0<java.lang.Double> defaultValue);
method public inline float getFloat(String key);
@@ -37,6 +40,7 @@
method public inline java.util.List<java.lang.String> getStringListOrElse(String key, kotlin.jvm.functions.Function0<? extends java.util.List<java.lang.String>> defaultValue);
method public inline String getStringOrElse(String key, kotlin.jvm.functions.Function0<java.lang.String> defaultValue);
method public inline boolean isEmpty();
+ method public inline boolean isNull(String key);
method public inline int size();
property public final android.os.Bundle source;
}
@@ -84,11 +88,13 @@
method public android.os.Bundle getSource();
method public inline void putAll(android.os.Bundle values);
method public inline void putBoolean(String key, boolean value);
+ method public inline void putChar(String key, char value);
method public inline void putDouble(String key, double value);
method public inline void putFloat(String key, float value);
method public inline void putInt(String key, int value);
method public inline void putIntList(String key, java.util.List<java.lang.Integer> values);
method public inline void putLong(String key, long value);
+ method public inline void putNull(String key);
method public inline <reified T extends android.os.Parcelable> void putParcelable(String key, T value);
method public inline <reified T extends android.os.Parcelable> void putParcelableList(String key, java.util.List<? extends T> values);
method public inline void putSavedState(String key, android.os.Bundle value);
@@ -99,7 +105,7 @@
}
public final class SavedState_androidKt {
- method public static inline android.os.Bundle savedState(optional kotlin.jvm.functions.Function1<? super androidx.savedstate.SavedStateWriter,kotlin.Unit> block);
+ method public static inline android.os.Bundle savedState(optional java.util.Map<java.lang.String,? extends java.lang.Object?> initialState, optional kotlin.jvm.functions.Function1<? super androidx.savedstate.SavedStateWriter,kotlin.Unit> builderAction);
}
public final class ViewKt {
diff --git a/savedstate/savedstate/api/restricted_current.txt b/savedstate/savedstate/api/restricted_current.txt
index 47dcf78..ea3533a 100644
--- a/savedstate/savedstate/api/restricted_current.txt
+++ b/savedstate/savedstate/api/restricted_current.txt
@@ -13,8 +13,11 @@
@kotlin.jvm.JvmInline public final value class SavedStateReader {
ctor public SavedStateReader(android.os.Bundle source);
method public inline operator boolean contains(String key);
+ method public boolean contentDeepEquals(android.os.Bundle other);
method public inline boolean getBoolean(String key);
method public inline boolean getBooleanOrElse(String key, kotlin.jvm.functions.Function0<java.lang.Boolean> defaultValue);
+ method public inline char getChar(String key);
+ method public inline char getCharOrElse(String key, kotlin.jvm.functions.Function0<java.lang.Character> defaultValue);
method public inline double getDouble(String key);
method public inline double getDoubleOrElse(String key, kotlin.jvm.functions.Function0<java.lang.Double> defaultValue);
method public inline float getFloat(String key);
@@ -41,10 +44,15 @@
method public inline java.util.List<java.lang.String> getStringListOrElse(String key, kotlin.jvm.functions.Function0<? extends java.util.List<java.lang.String>> defaultValue);
method public inline String getStringOrElse(String key, kotlin.jvm.functions.Function0<java.lang.String> defaultValue);
method public inline boolean isEmpty();
+ method public inline boolean isNull(String key);
method public inline int size();
property public final android.os.Bundle source;
}
+ public final class SavedStateReader_androidKt {
+ method @kotlin.PublishedApi internal static boolean contentDeepEquals(android.os.Bundle, android.os.Bundle other);
+ }
+
public final class SavedStateRegistry {
method @MainThread public android.os.Bundle? consumeRestoredStateForKey(String key);
method public androidx.savedstate.SavedStateRegistry.SavedStateProvider? getSavedStateProvider(String key);
@@ -88,11 +96,13 @@
method public android.os.Bundle getSource();
method public inline void putAll(android.os.Bundle values);
method public inline void putBoolean(String key, boolean value);
+ method public inline void putChar(String key, char value);
method public inline void putDouble(String key, double value);
method public inline void putFloat(String key, float value);
method public inline void putInt(String key, int value);
method public inline void putIntList(String key, java.util.List<java.lang.Integer> values);
method public inline void putLong(String key, long value);
+ method public inline void putNull(String key);
method public inline <reified T extends android.os.Parcelable> void putParcelable(String key, T value);
method public inline <reified T extends android.os.Parcelable> void putParcelableList(String key, java.util.List<? extends T> values);
method public inline void putSavedState(String key, android.os.Bundle value);
@@ -104,7 +114,7 @@
}
public final class SavedState_androidKt {
- method public static inline android.os.Bundle savedState(optional kotlin.jvm.functions.Function1<? super androidx.savedstate.SavedStateWriter,kotlin.Unit> block);
+ method public static inline android.os.Bundle savedState(optional java.util.Map<java.lang.String,? extends java.lang.Object?> initialState, optional kotlin.jvm.functions.Function1<? super androidx.savedstate.SavedStateWriter,kotlin.Unit> builderAction);
}
public final class ViewKt {
@@ -124,6 +134,7 @@
method public inline <reified T> T getValueFromSavedState(String key, kotlin.jvm.functions.Function0<? extends T?> currentValue, kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> contains, kotlin.jvm.functions.Function0<? extends T> defaultValue);
method public inline Void keyNotFoundError(String key);
field public static final boolean DEFAULT_BOOLEAN = false;
+ field public static final char DEFAULT_CHAR = 0; // 0x0000 '\u0000'
field public static final double DEFAULT_DOUBLE = 0.0;
field public static final float DEFAULT_FLOAT = 0.0f;
field public static final int DEFAULT_INT = 0; // 0x0
diff --git a/savedstate/savedstate/bcv/native/current.txt b/savedstate/savedstate/bcv/native/current.txt
index 1a70510..a72f706 100644
--- a/savedstate/savedstate/bcv/native/current.txt
+++ b/savedstate/savedstate/bcv/native/current.txt
@@ -12,10 +12,10 @@
}
final class androidx.savedstate/SavedState { // androidx.savedstate/SavedState|null[0]
- constructor <init>(kotlin.collections/MutableMap<kotlin/String, kotlin/Any> = ...) // androidx.savedstate/SavedState.<init>|<init>(kotlin.collections.MutableMap<kotlin.String,kotlin.Any>){}[0]
+ constructor <init>(kotlin.collections/MutableMap<kotlin/String, kotlin/Any?> = ...) // androidx.savedstate/SavedState.<init>|<init>(kotlin.collections.MutableMap<kotlin.String,kotlin.Any?>){}[0]
final val map // androidx.savedstate/SavedState.map|{}map[0]
- final fun <get-map>(): kotlin.collections/MutableMap<kotlin/String, kotlin/Any> // androidx.savedstate/SavedState.map.<get-map>|<get-map>(){}[0]
+ final fun <get-map>(): kotlin.collections/MutableMap<kotlin/String, kotlin/Any?> // androidx.savedstate/SavedState.map.<get-map>|<get-map>(){}[0]
}
final class androidx.savedstate/SavedStateRegistry { // androidx.savedstate/SavedStateRegistry|null[0]
@@ -51,6 +51,7 @@
final val source // androidx.savedstate/SavedStateReader.source|{}source[0]
final fun <get-source>(): androidx.savedstate/SavedState // androidx.savedstate/SavedStateReader.source.<get-source>|<get-source>(){}[0]
+ final fun contentDeepEquals(androidx.savedstate/SavedState): kotlin/Boolean // androidx.savedstate/SavedStateReader.contentDeepEquals|contentDeepEquals(androidx.savedstate.SavedState){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.savedstate/SavedStateReader.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // androidx.savedstate/SavedStateReader.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.savedstate/SavedStateReader.toString|toString(){}[0]
@@ -61,6 +62,8 @@
final inline fun contains(kotlin/String): kotlin/Boolean // androidx.savedstate/SavedStateReader.contains|contains(kotlin.String){}[0]
final inline fun getBoolean(kotlin/String): kotlin/Boolean // androidx.savedstate/SavedStateReader.getBoolean|getBoolean(kotlin.String){}[0]
final inline fun getBooleanOrElse(kotlin/String, kotlin/Function0<kotlin/Boolean>): kotlin/Boolean // androidx.savedstate/SavedStateReader.getBooleanOrElse|getBooleanOrElse(kotlin.String;kotlin.Function0<kotlin.Boolean>){}[0]
+ final inline fun getChar(kotlin/String): kotlin/Char // androidx.savedstate/SavedStateReader.getChar|getChar(kotlin.String){}[0]
+ final inline fun getCharOrElse(kotlin/String, kotlin/Function0<kotlin/Char>): kotlin/Char // androidx.savedstate/SavedStateReader.getCharOrElse|getCharOrElse(kotlin.String;kotlin.Function0<kotlin.Char>){}[0]
final inline fun getDouble(kotlin/String): kotlin/Double // androidx.savedstate/SavedStateReader.getDouble|getDouble(kotlin.String){}[0]
final inline fun getDoubleOrElse(kotlin/String, kotlin/Function0<kotlin/Double>): kotlin/Double // androidx.savedstate/SavedStateReader.getDoubleOrElse|getDoubleOrElse(kotlin.String;kotlin.Function0<kotlin.Double>){}[0]
final inline fun getFloat(kotlin/String): kotlin/Float // androidx.savedstate/SavedStateReader.getFloat|getFloat(kotlin.String){}[0]
@@ -78,6 +81,7 @@
final inline fun getStringListOrElse(kotlin/String, kotlin/Function0<kotlin.collections/List<kotlin/String>>): kotlin.collections/List<kotlin/String> // androidx.savedstate/SavedStateReader.getStringListOrElse|getStringListOrElse(kotlin.String;kotlin.Function0<kotlin.collections.List<kotlin.String>>){}[0]
final inline fun getStringOrElse(kotlin/String, kotlin/Function0<kotlin/String>): kotlin/String // androidx.savedstate/SavedStateReader.getStringOrElse|getStringOrElse(kotlin.String;kotlin.Function0<kotlin.String>){}[0]
final inline fun isEmpty(): kotlin/Boolean // androidx.savedstate/SavedStateReader.isEmpty|isEmpty(){}[0]
+ final inline fun isNull(kotlin/String): kotlin/Boolean // androidx.savedstate/SavedStateReader.isNull|isNull(kotlin.String){}[0]
final inline fun size(): kotlin/Int // androidx.savedstate/SavedStateReader.size|size(){}[0]
}
@@ -93,11 +97,13 @@
final inline fun clear() // androidx.savedstate/SavedStateWriter.clear|clear(){}[0]
final inline fun putAll(androidx.savedstate/SavedState) // androidx.savedstate/SavedStateWriter.putAll|putAll(androidx.savedstate.SavedState){}[0]
final inline fun putBoolean(kotlin/String, kotlin/Boolean) // androidx.savedstate/SavedStateWriter.putBoolean|putBoolean(kotlin.String;kotlin.Boolean){}[0]
+ final inline fun putChar(kotlin/String, kotlin/Char) // androidx.savedstate/SavedStateWriter.putChar|putChar(kotlin.String;kotlin.Char){}[0]
final inline fun putDouble(kotlin/String, kotlin/Double) // androidx.savedstate/SavedStateWriter.putDouble|putDouble(kotlin.String;kotlin.Double){}[0]
final inline fun putFloat(kotlin/String, kotlin/Float) // androidx.savedstate/SavedStateWriter.putFloat|putFloat(kotlin.String;kotlin.Float){}[0]
final inline fun putInt(kotlin/String, kotlin/Int) // androidx.savedstate/SavedStateWriter.putInt|putInt(kotlin.String;kotlin.Int){}[0]
final inline fun putIntList(kotlin/String, kotlin.collections/List<kotlin/Int>) // androidx.savedstate/SavedStateWriter.putIntList|putIntList(kotlin.String;kotlin.collections.List<kotlin.Int>){}[0]
final inline fun putLong(kotlin/String, kotlin/Long) // androidx.savedstate/SavedStateWriter.putLong|putLong(kotlin.String;kotlin.Long){}[0]
+ final inline fun putNull(kotlin/String) // androidx.savedstate/SavedStateWriter.putNull|putNull(kotlin.String){}[0]
final inline fun putSavedState(kotlin/String, androidx.savedstate/SavedState) // androidx.savedstate/SavedStateWriter.putSavedState|putSavedState(kotlin.String;androidx.savedstate.SavedState){}[0]
final inline fun putString(kotlin/String, kotlin/String) // androidx.savedstate/SavedStateWriter.putString|putString(kotlin.String;kotlin.String){}[0]
final inline fun putStringList(kotlin/String, kotlin.collections/List<kotlin/String>) // androidx.savedstate/SavedStateWriter.putStringList|putStringList(kotlin.String;kotlin.collections.List<kotlin.String>){}[0]
@@ -107,6 +113,8 @@
final object androidx.savedstate.internal/SavedStateUtils { // androidx.savedstate.internal/SavedStateUtils|null[0]
final const val DEFAULT_BOOLEAN // androidx.savedstate.internal/SavedStateUtils.DEFAULT_BOOLEAN|{}DEFAULT_BOOLEAN[0]
final fun <get-DEFAULT_BOOLEAN>(): kotlin/Boolean // androidx.savedstate.internal/SavedStateUtils.DEFAULT_BOOLEAN.<get-DEFAULT_BOOLEAN>|<get-DEFAULT_BOOLEAN>(){}[0]
+ final const val DEFAULT_CHAR // androidx.savedstate.internal/SavedStateUtils.DEFAULT_CHAR|{}DEFAULT_CHAR[0]
+ final fun <get-DEFAULT_CHAR>(): kotlin/Char // androidx.savedstate.internal/SavedStateUtils.DEFAULT_CHAR.<get-DEFAULT_CHAR>|<get-DEFAULT_CHAR>(){}[0]
final const val DEFAULT_DOUBLE // androidx.savedstate.internal/SavedStateUtils.DEFAULT_DOUBLE|{}DEFAULT_DOUBLE[0]
final fun <get-DEFAULT_DOUBLE>(): kotlin/Double // androidx.savedstate.internal/SavedStateUtils.DEFAULT_DOUBLE.<get-DEFAULT_DOUBLE>|<get-DEFAULT_DOUBLE>(){}[0]
final const val DEFAULT_FLOAT // androidx.savedstate.internal/SavedStateUtils.DEFAULT_FLOAT|{}DEFAULT_FLOAT[0]
@@ -126,4 +134,4 @@
final inline fun <#A: kotlin/Any?> (androidx.savedstate/SavedState).androidx.savedstate/write(kotlin/Function1<androidx.savedstate/SavedStateWriter, #A>): #A // androidx.savedstate/write|[email protected](kotlin.Function1<androidx.savedstate.SavedStateWriter,0:0>){0§<kotlin.Any?>}[0]
final inline fun <#A: kotlin/Any?> (androidx.savedstate/SavedStateReader).androidx.savedstate/write(kotlin/Function1<androidx.savedstate/SavedStateWriter, #A>): #A // androidx.savedstate/write|[email protected](kotlin.Function1<androidx.savedstate.SavedStateWriter,0:0>){0§<kotlin.Any?>}[0]
final inline fun <#A: kotlin/Any?> (androidx.savedstate/SavedStateWriter).androidx.savedstate/read(kotlin/Function1<androidx.savedstate/SavedStateReader, #A>): #A // androidx.savedstate/read|[email protected](kotlin.Function1<androidx.savedstate.SavedStateReader,0:0>){0§<kotlin.Any?>}[0]
-final inline fun androidx.savedstate/savedState(kotlin/Function1<androidx.savedstate/SavedStateWriter, kotlin/Unit> = ...): androidx.savedstate/SavedState // androidx.savedstate/savedState|savedState(kotlin.Function1<androidx.savedstate.SavedStateWriter,kotlin.Unit>){}[0]
+final inline fun androidx.savedstate/savedState(kotlin.collections/Map<kotlin/String, kotlin/Any?> = ..., kotlin/Function1<androidx.savedstate/SavedStateWriter, kotlin/Unit> = ...): androidx.savedstate/SavedState // androidx.savedstate/savedState|savedState(kotlin.collections.Map<kotlin.String,kotlin.Any?>;kotlin.Function1<androidx.savedstate.SavedStateWriter,kotlin.Unit>){}[0]
diff --git a/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/SavedState.android.kt b/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/SavedState.android.kt
index 6d51126..b181e9b 100644
--- a/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/SavedState.android.kt
+++ b/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/SavedState.android.kt
@@ -14,9 +14,23 @@
* limitations under the License.
*/
+@file:Suppress("NOTHING_TO_INLINE")
+
package androidx.savedstate
+import androidx.core.os.bundleOf
+
public actual typealias SavedState = android.os.Bundle
-public actual inline fun savedState(block: SavedStateWriter.() -> Unit): SavedState =
- SavedState().apply { write(block) }
+public actual inline fun savedState(
+ initialState: Map<String, Any?>,
+ builderAction: SavedStateWriter.() -> Unit,
+): SavedState {
+ val pairs =
+ if (initialState.isEmpty()) {
+ emptyArray()
+ } else {
+ initialState.map { (key, value) -> key to value }.toTypedArray()
+ }
+ return bundleOf(*pairs).apply { write(builderAction) }
+}
diff --git a/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/SavedStateReader.android.kt b/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/SavedStateReader.android.kt
index c17f0fa..430c4ef 100644
--- a/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/SavedStateReader.android.kt
+++ b/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/SavedStateReader.android.kt
@@ -33,7 +33,15 @@
}
actual inline fun getBooleanOrElse(key: String, defaultValue: () -> Boolean): Boolean {
- return getSingleResultOrElse(key, defaultValue) { source.getBoolean(key) }
+ return getSingleResultOrElse(key, defaultValue) { source.getBoolean(key, defaultValue()) }
+ }
+
+ actual inline fun getChar(key: String): Char {
+ return getSingleResultOrThrow(key) { source.getChar(key, SavedStateUtils.DEFAULT_CHAR) }
+ }
+
+ actual inline fun getCharOrElse(key: String, defaultValue: () -> Char): Char {
+ return getSingleResultOrElse(key, defaultValue) { source.getChar(key, defaultValue()) }
}
actual inline fun getDouble(key: String): Double {
@@ -41,7 +49,7 @@
}
actual inline fun getDoubleOrElse(key: String, defaultValue: () -> Double): Double {
- return getSingleResultOrElse(key, defaultValue) { source.getDouble(key) }
+ return getSingleResultOrElse(key, defaultValue) { source.getDouble(key, defaultValue()) }
}
actual inline fun getFloat(key: String): Float {
@@ -49,7 +57,7 @@
}
actual inline fun getFloatOrElse(key: String, defaultValue: () -> Float): Float {
- return getSingleResultOrElse(key, defaultValue) { source.getFloat(key) }
+ return getSingleResultOrElse(key, defaultValue) { source.getFloat(key, defaultValue()) }
}
actual inline fun getInt(key: String): Int {
@@ -57,15 +65,15 @@
}
actual inline fun getIntOrElse(key: String, defaultValue: () -> Int): Int {
- return getSingleResultOrElse(key, defaultValue) { source.getInt(key) }
+ return getSingleResultOrElse(key, defaultValue) { source.getInt(key, defaultValue()) }
}
actual inline fun getLong(key: String): Long {
- return getSingleResultOrThrow(key) { source.getLong(key) }
+ return getSingleResultOrThrow(key) { source.getLong(key, SavedStateUtils.DEFAULT_LONG) }
}
actual inline fun getLongOrElse(key: String, defaultValue: () -> Long): Long {
- return getSingleResultOrElse(key, defaultValue) { source.getLong(key) }
+ return getSingleResultOrElse(key, defaultValue) { source.getLong(key, defaultValue()) }
}
/**
@@ -102,7 +110,7 @@
}
actual inline fun getStringOrElse(key: String, defaultValue: () -> String): String {
- return getSingleResultOrElse(key, defaultValue) { source.getString(key) }
+ return getSingleResultOrElse(key, defaultValue) { source.getString(key, defaultValue()) }
}
actual inline fun getIntList(key: String): List<Int> {
@@ -169,8 +177,17 @@
actual inline fun isEmpty(): Boolean = source.isEmpty
+ actual inline fun isNull(key: String): Boolean {
+ // Using `getString` to check for `null` is unreliable as it returns null for type
+ // mismatches. To reliably determine if the value is actually `null`, we use the
+ // deprecated `Bundle.get`.
+ @Suppress("DEPRECATION") return contains(key) && source[key] == null
+ }
+
actual inline operator fun contains(key: String): Boolean = source.containsKey(key)
+ actual fun contentDeepEquals(other: SavedState): Boolean = source.contentDeepEquals(other)
+
@PublishedApi
internal inline fun <reified T> getSingleResultOrThrow(
key: String,
@@ -221,3 +238,23 @@
defaultValue = { defaultValue() },
)
}
+
+@PublishedApi
+internal fun SavedState.contentDeepEquals(other: SavedState): Boolean {
+ if (this === other) return true
+ if (this.size() != other.size()) return false
+
+ for (k in this.keySet()) {
+ @Suppress("DEPRECATION") val v1 = this[k]
+ @Suppress("DEPRECATION") val v2 = other[k]
+
+ when {
+ v1 === v2 -> continue
+ v1 == null || v2 == null -> return false
+ v1 is SavedState && v2 is SavedState -> if (!v1.contentDeepEquals(v2)) return false
+ v1 is Array<*> && v2 is Array<*> -> if (!v1.contentDeepEquals(v2)) return false
+ else -> if (v1 != v2) return false
+ }
+ }
+ return true
+}
diff --git a/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/SavedStateWriter.android.kt b/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/SavedStateWriter.android.kt
index 68df552..d0b5687 100644
--- a/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/SavedStateWriter.android.kt
+++ b/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/SavedStateWriter.android.kt
@@ -26,6 +26,10 @@
source.putBoolean(key, value)
}
+ actual inline fun putChar(key: String, value: Char) {
+ source.putChar(key, value)
+ }
+
actual inline fun putDouble(key: String, value: Double) {
source.putDouble(key, value)
}
@@ -42,6 +46,10 @@
source.putLong(key, value)
}
+ actual inline fun putNull(key: String) {
+ source.putString(key, null)
+ }
+
/**
* Stores an [Parcelable] value associated with the specified key in the [SavedState].
*
diff --git a/savedstate/savedstate/src/androidUnitTest/kotlin/androidx/savedstate/ParcelableSavedStateTest.android.kt b/savedstate/savedstate/src/androidUnitTest/kotlin/androidx/savedstate/ParcelableSavedStateTest.android.kt
index 48f76bf..73d9c8d 100644
--- a/savedstate/savedstate/src/androidUnitTest/kotlin/androidx/savedstate/ParcelableSavedStateTest.android.kt
+++ b/savedstate/savedstate/src/androidUnitTest/kotlin/androidx/savedstate/ParcelableSavedStateTest.android.kt
@@ -64,6 +64,14 @@
}
@Test
+ fun getParcelableOrElse_whenSet_differentType_returnsDefault() {
+ val underTest = savedState { putInt(KEY_1, Int.MAX_VALUE) }
+ val actual = underTest.read { getParcelableOrElse(KEY_1) { PARCELABLE_VALUE_1 } }
+
+ assertThat(actual).isEqualTo(PARCELABLE_VALUE_1)
+ }
+
+ @Test
fun getParcelableList_whenSet_returns() {
val expected = List(size = 5) { idx -> TestParcelable(idx) }
@@ -81,7 +89,7 @@
}
@Test
- fun getListofParcelable_whenSet_differentType_throws() {
+ fun getList_whenSet_differentType_throws() {
val underTest = savedState { putInt(KEY_1, Int.MAX_VALUE) }
assertThrows<IllegalStateException> {
@@ -108,6 +116,14 @@
assertThat(actual).isEqualTo(emptyList<TestParcelable>())
}
+ @Test
+ fun getListOrElse_whenSet_differentType_throws() {
+ val underTest = savedState { putInt(KEY_1, Int.MAX_VALUE) }
+ val actual = underTest.read { getParcelableListOrElse(KEY_1) { emptyList() } }
+
+ assertThat(actual).isEqualTo(emptyList<Parcelable>())
+ }
+
private companion object {
const val KEY_1 = "KEY_1"
val PARCELABLE_VALUE_1 = TestParcelable(value = Int.MIN_VALUE)
diff --git a/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/SavedState.kt b/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/SavedState.kt
index 710bdd0..2dd1c03 100644
--- a/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/SavedState.kt
+++ b/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/SavedState.kt
@@ -31,8 +31,24 @@
*/
public expect class SavedState
-/** Constructs an empty [SavedState] instance. */
-public expect inline fun savedState(block: SavedStateWriter.() -> Unit = {}): SavedState
+/**
+ * Builds a new [SavedState] with the specified [initialState], given as a [Map] of [String] keys
+ * and [Any] value.
+ *
+ * Allows further modification of the state using the [builderAction].
+ *
+ * **IMPORTANT:** The [SavedStateWriter] passed as a receiver to the [builderAction] is valid only
+ * inside that function. Using it outside of the function may produce an unspecified behavior.
+ *
+ * @param initialState An initial map of key-value pairs to populate the state. Defaults to an empty
+ * map.
+ * @param builderAction A lambda function with a [SavedStateWriter] receiver to modify the state.
+ * @return A [SavedState] instance containing the initialized key-value pairs.
+ */
+public expect inline fun savedState(
+ initialState: Map<String, Any?> = emptyMap(),
+ builderAction: SavedStateWriter.() -> Unit = {},
+): SavedState
/** Creates a new [SavedStateReader] for the [SavedState]. */
public fun SavedState.reader(): SavedStateReader = SavedStateReader(source = this)
diff --git a/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/SavedStateReader.kt b/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/SavedStateReader.kt
index e3f37ac..cd7cc0a 100644
--- a/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/SavedStateReader.kt
+++ b/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/SavedStateReader.kt
@@ -52,6 +52,27 @@
public inline fun getBooleanOrElse(key: String, defaultValue: () -> Boolean): Boolean
/**
+ * Retrieves a [Char] value associated with the specified [key]. Throws an
+ * [IllegalStateException] if the [key] doesn't exist.
+ *
+ * @param key The [key] to retrieve the value for.
+ * @return The [Char] value associated with the [key].
+ * @throws IllegalStateException If the [key] is not found.
+ */
+ public inline fun getChar(key: String): Char
+
+ /**
+ * Retrieves a [Char] value associated with the specified [key], or a default value if the [key]
+ * doesn't exist.
+ *
+ * @param key The [key] to retrieve the value for.
+ * @param defaultValue A function providing the default value if the [key] is not found.
+ * @return The [Char] value associated with the [key], or the default value if the [key] is not
+ * found.
+ */
+ public inline fun getCharOrElse(key: String, defaultValue: () -> Char): Char
+
+ /**
* Retrieves a [Double] value associated with the specified [key]. Throws an
* [IllegalStateException] if the [key] doesn't exist.
*
@@ -239,10 +260,31 @@
public inline fun isEmpty(): Boolean
/**
+ * Checks if the [SavedState] contains a null reference for the specified [key].
+ *
+ * @param key The [key] to check for.
+ * @return `true` if the [SavedState] contains a null reference for the [key], `false`
+ * otherwise.
+ */
+ public inline fun isNull(key: String): Boolean
+
+ /**
* Checks if the [SavedState] contains the specified [key].
*
* @param key The [key] to check for.
* @return `true` if the [SavedState] contains the [key], `false` otherwise.
*/
public inline operator fun contains(key: String): Boolean
+
+ /**
+ * Checks if the two specified [SavedState] are *deeply* equal to one another.
+ *
+ * Two [SavedState] are considered deeply equal if they have the same size, and elements at
+ * corresponding keys are deeply equal. That is, if two corresponding elements are nested
+ * [SavedState], they are also compared deeply.
+ *
+ * @param other the object to compare deeply with this.
+ * @return `true` if the two are deeply equal, `false` otherwise.
+ */
+ public fun contentDeepEquals(other: SavedState): Boolean
}
diff --git a/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/SavedStateWriter.kt b/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/SavedStateWriter.kt
index bf7aff6..f4d7c44 100644
--- a/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/SavedStateWriter.kt
+++ b/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/SavedStateWriter.kt
@@ -38,6 +38,8 @@
*/
public inline fun putBoolean(key: String, value: Boolean)
+ public inline fun putChar(key: String, value: Char)
+
/**
* Stores a double value associated with the specified key in the [SavedState].
*
@@ -71,6 +73,13 @@
public inline fun putLong(key: String, value: Long)
/**
+ * Stores a null reference associated with the specified key in the [SavedState].
+ *
+ * @param key The key to associate the null reference.
+ */
+ public inline fun putNull(key: String)
+
+ /**
* Stores a string value associated with the specified key in the [SavedState].
*
* @param key The key to associate the value with.
diff --git a/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/internal/SavedStateUtils.kt b/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/internal/SavedStateUtils.kt
index bf602bd..44326b9 100644
--- a/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/internal/SavedStateUtils.kt
+++ b/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/internal/SavedStateUtils.kt
@@ -20,6 +20,7 @@
internal object SavedStateUtils {
const val DEFAULT_BOOLEAN = false
+ const val DEFAULT_CHAR: Char = 0.toChar()
const val DEFAULT_FLOAT = 0F
const val DEFAULT_DOUBLE = 0.0
const val DEFAULT_INT = 0
diff --git a/savedstate/savedstate/src/commonTest/kotlin/androidx/savedstate/SavedStateTest.kt b/savedstate/savedstate/src/commonTest/kotlin/androidx/savedstate/SavedStateTest.kt
index b40f557..8991f3b 100644
--- a/savedstate/savedstate/src/commonTest/kotlin/androidx/savedstate/SavedStateTest.kt
+++ b/savedstate/savedstate/src/commonTest/kotlin/androidx/savedstate/SavedStateTest.kt
@@ -95,6 +95,89 @@
assertThat(underTest.read { isEmpty() }).isTrue()
}
+ @Test
+ fun contentDeepEquals_withEqualContent_returnsTrue() {
+ val sharedState = savedState {
+ putInt(KEY_1, Int.MAX_VALUE)
+ putInt(KEY_2, Int.MAX_VALUE)
+ }
+ val state1 = savedState {
+ putInt(KEY_1, Int.MAX_VALUE)
+ putInt(KEY_2, Int.MAX_VALUE)
+ putSavedState(KEY_3, sharedState)
+ }
+ val state2 = savedState {
+ putInt(KEY_1, Int.MAX_VALUE)
+ putInt(KEY_2, Int.MAX_VALUE)
+ putSavedState(KEY_3, sharedState)
+ }
+
+ val contentDeepEquals = state1.read { contentDeepEquals(state2) }
+
+ assertThat(contentDeepEquals).isTrue()
+ }
+
+ @Test
+ fun contentDeepEquals_withMissingKey_returnsFalse() {
+ val sharedState = savedState {
+ putInt(KEY_1, Int.MAX_VALUE)
+ putInt(KEY_2, Int.MAX_VALUE)
+ }
+ val state1 = savedState {
+ putInt(KEY_1, Int.MAX_VALUE)
+ putInt(KEY_2, Int.MAX_VALUE)
+ putSavedState(KEY_3, sharedState)
+ }
+ val state2 = savedState {
+ putInt(KEY_1, Int.MAX_VALUE)
+ putSavedState(KEY_3, sharedState)
+ }
+
+ val contentDeepEquals = state1.read { contentDeepEquals(state2) }
+
+ assertThat(contentDeepEquals).isFalse()
+ }
+
+ @Test
+ fun contentDeepEquals_withDifferentContent_returnsFalse() {
+ val sharedState = savedState {
+ putInt(KEY_1, Int.MAX_VALUE)
+ putInt(KEY_2, Int.MAX_VALUE)
+ }
+ val state1 = savedState {
+ putInt(KEY_1, Int.MAX_VALUE)
+ putInt(KEY_2, Int.MAX_VALUE)
+ putSavedState(KEY_3, sharedState)
+ }
+ val state2 = savedState {
+ putFloat(KEY_1, Float.MAX_VALUE)
+ putFloat(KEY_2, Float.MAX_VALUE)
+ putSavedState(KEY_3, sharedState)
+ }
+
+ val contentDeepEquals = state1.read { contentDeepEquals(state2) }
+
+ assertThat(contentDeepEquals).isFalse()
+ }
+
+ @Test
+ fun contentDeepEquals_withEmptyContent_returnsFalse() {
+ val sharedState = savedState {
+ putInt(KEY_1, Int.MAX_VALUE)
+ putInt(KEY_2, Int.MAX_VALUE)
+ }
+ val state1 = savedState {
+ putInt(KEY_1, Int.MAX_VALUE)
+ putInt(KEY_2, Int.MAX_VALUE)
+ putSavedState(KEY_3, sharedState)
+ }
+ val state2 = savedState()
+
+ val contentDeepEquals = state1.read { contentDeepEquals(state2) }
+
+ assertThat(contentDeepEquals).isFalse()
+ }
+
// region getters and setters
@Test
fun getBoolean_whenSet_returns() {
@@ -131,9 +214,61 @@
@Test
fun getBooleanOrElse_whenNotSet_returnsElse() {
- val actual = savedState().read { getBooleanOrElse(KEY_1) { false } }
+ val actual = savedState().read { getBooleanOrElse(KEY_1) { true } }
- assertThat(actual).isFalse()
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun getBooleanOrElse_whenSet_differentType_returnsElse() {
+ val underTest = savedState { putInt(KEY_1, Int.MAX_VALUE) }
+ val actual = underTest.read { getBooleanOrElse(KEY_1) { true } }
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun getChar_whenSet_returns() {
+ val underTest = savedState { putChar(KEY_1, Char.MAX_VALUE) }
+ val actual = underTest.read { getChar(KEY_1) }
+
+ assertThat(actual).isEqualTo(Char.MAX_VALUE)
+ }
+
+ @Test
+ fun getChar_whenNotSet_throws() {
+ assertThrows<IllegalStateException> { savedState().read { getChar(KEY_1) } }
+ }
+
+ @Test
+ fun getChar_whenSet_differentType_returnsDefault() {
+ val underTest = savedState { putInt(KEY_1, Int.MIN_VALUE) }
+ val actual = underTest.read { getChar(KEY_1) }
+
+ assertThat(actual).isEqualTo(SavedStateUtils.DEFAULT_CHAR)
+ }
+
+ @Test
+ fun getCharOrElse_whenSet_returns() {
+ val underTest = savedState { putChar(KEY_1, Char.MAX_VALUE) }
+ val actual = underTest.read { getCharOrElse(KEY_1) { Char.MIN_VALUE } }
+
+ assertThat(actual).isEqualTo(Char.MAX_VALUE)
+ }
+
+ @Test
+ fun getCharOrElse_whenNotSet_returnsElse() {
+ val actual = savedState().read { getCharOrElse(KEY_1) { Char.MIN_VALUE } }
+
+ assertThat(actual).isEqualTo(Char.MIN_VALUE)
+ }
+
+ @Test
+ fun getCharOrElse_whenSet_differentType_returnsElse() {
+ val underTest = savedState { putInt(KEY_1, Int.MAX_VALUE) }
+ val actual = underTest.read { getCharOrElse(KEY_1) { Char.MAX_VALUE } }
+
+ assertThat(actual).isEqualTo(Char.MAX_VALUE)
}
@Test
@@ -173,6 +308,14 @@
}
@Test
+ fun getDoubleOrElse_whenSet_differentType_returnsElse() {
+ val underTest = savedState { putInt(KEY_1, Int.MAX_VALUE) }
+ val actual = underTest.read { getDoubleOrElse(KEY_1) { Double.MIN_VALUE } }
+
+ assertThat(actual).isEqualTo(Double.MIN_VALUE)
+ }
+
+ @Test
fun getFloat_whenSet_returns() {
val underTest = savedState { putFloat(KEY_1, Float.MAX_VALUE) }
val actual = underTest.read { getFloat(KEY_1) }
@@ -209,6 +352,14 @@
}
@Test
+ fun getFloatOrElse_whenSet_differentType_returnsElse() {
+ val underTest = savedState { putInt(KEY_1, Int.MAX_VALUE) }
+ val actual = underTest.read { getFloatOrElse(KEY_1) { Float.MIN_VALUE } }
+
+ assertThat(actual).isEqualTo(Float.MIN_VALUE)
+ }
+
+ @Test
fun getInt_whenSet_returns() {
val underTest = savedState { putInt(KEY_1, Int.MAX_VALUE) }
val actual = underTest.read { getInt(KEY_1) }
@@ -245,6 +396,14 @@
}
@Test
+ fun getIntOrElse_whenSet_differentType_returnsElse() {
+ val underTest = savedState { putBoolean(KEY_1, false) }
+ val actual = underTest.read { getIntOrElse(KEY_1) { Int.MIN_VALUE } }
+
+ assertThat(actual).isEqualTo(Int.MIN_VALUE)
+ }
+
+ @Test
fun getLong_whenSet_returns() {
val underTest = savedState { putLong(KEY_1, Long.MAX_VALUE) }
val actual = underTest.read { getLong(KEY_1) }
@@ -281,6 +440,38 @@
}
@Test
+ fun getLongOrElse_whenSet_differentType_returnsElse() {
+ val underTest = savedState { putBoolean(KEY_1, false) }
+ val actual = underTest.read { getLongOrElse(KEY_1) { Long.MIN_VALUE } }
+
+ assertThat(actual).isEqualTo(Long.MIN_VALUE)
+ }
+
+ @Test
+ fun putNull_whenSet_returnsTrue() {
+ val underTest = savedState { putNull(KEY_1) }
+ val actual = underTest.read { isNull(KEY_1) }
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun getNull_whenSet_nonNull_returnsFalse() {
+ val underTest = savedState { putBoolean(KEY_1, true) }
+ val actual = underTest.read { isNull(KEY_1) }
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun putNull_whenNotSet_returnsFalse() {
+ val underTest = savedState()
+ val actual = underTest.read { isNull(KEY_1) }
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
fun getString_whenSet_returns() {
val underTest = savedState { putString(KEY_1, STRING_VALUE) }
val actual = underTest.read { getString(KEY_1) }
@@ -316,6 +507,15 @@
}
@Test
+ fun getStringOrElse_whenSet_differentType_returnsElse() {
+ val underTest = savedState { putInt(KEY_1, Int.MAX_VALUE) }
+
+ val actual = underTest.read { getStringOrElse(KEY_1) { STRING_VALUE } }
+
+ assertThat(actual).isEqualTo(STRING_VALUE)
+ }
+
+ @Test
fun getIntList_whenSet_returns() {
val expected = List(size = 5) { idx -> idx }
@@ -355,6 +555,16 @@
}
@Test
+ fun getIntOrElseList_whenSet_differentType_returnsElse() {
+ val expected = Int.MAX_VALUE
+
+ val underTest = savedState { putInt(KEY_1, expected) }
+ val actual = underTest.read { getIntListOrElse(KEY_1) { emptyList() } }
+
+ assertThat(actual).isEqualTo(emptyList<Int>())
+ }
+
+ @Test
fun getStringList_whenSet_returns() {
val underTest = savedState { putStringList(KEY_1, LIST_STRING_VALUE) }
val actual = underTest.read { getStringList(KEY_1) }
@@ -392,6 +602,16 @@
}
@Test
+ fun getStringListOrElse_whenSet_differentType_returnsElse() {
+ val expected = Int.MAX_VALUE
+
+ val underTest = savedState { putInt(KEY_1, expected) }
+ val actual = underTest.read { getStringListOrElse(KEY_1) { emptyList() } }
+
+ assertThat(actual).isEqualTo(emptyList<String>())
+ }
+
+ @Test
fun getSavedState_whenSet_returns() {
val underTest = savedState { putSavedState(KEY_1, SAVED_STATE_VALUE) }
val actual = underTest.read { getSavedState(KEY_1) }
@@ -405,6 +625,13 @@
}
@Test
+ fun getSavedState_whenSet_differentType_throws() {
+ val underTest = savedState { putInt(KEY_1, Int.MAX_VALUE) }
+
+ assertThrows<IllegalStateException> { underTest.read { getSavedState(KEY_1) } }
+ }
+
+ @Test
fun getSavedStateOrElse_whenSet_returns() {
val underTest = savedState { putSavedState(KEY_1, SAVED_STATE_VALUE) }
val actual = underTest.read { getSavedStateOrElse(KEY_1) { savedState() } }
@@ -422,6 +649,16 @@
}
@Test
+ fun getSavedStateOrElse_whenSet_differentType_returnsElse() {
+ val expected = savedState()
+
+ val underTest = savedState { putInt(KEY_1, Int.MAX_VALUE) }
+ val actual = underTest.read { getSavedStateOrElse(KEY_1) { expected } }
+
+ assertThat(actual).isEqualTo(expected)
+ }
+
+ @Test
fun putAll() {
val previousState = savedState { putInt(KEY_1, Int.MAX_VALUE) }
@@ -436,6 +673,7 @@
private companion object {
const val KEY_1 = "KEY_1"
const val KEY_2 = "KEY_2"
+ const val KEY_3 = "KEY_3"
const val STRING_VALUE = "string-value"
val LIST_INT_VALUE = List(size = 5) { idx -> idx }
val LIST_STRING_VALUE = List(size = 5) { idx -> "index=$idx" }
diff --git a/savedstate/savedstate/src/nonAndroidMain/kotlin/androidx/savedstate/SavedState.nonAndroid.kt b/savedstate/savedstate/src/nonAndroidMain/kotlin/androidx/savedstate/SavedState.nonAndroid.kt
index 127e516..c8e4d106 100644
--- a/savedstate/savedstate/src/nonAndroidMain/kotlin/androidx/savedstate/SavedState.nonAndroid.kt
+++ b/savedstate/savedstate/src/nonAndroidMain/kotlin/androidx/savedstate/SavedState.nonAndroid.kt
@@ -14,11 +14,15 @@
* limitations under the License.
*/
+@file:Suppress("NOTHING_TO_INLINE")
+
package androidx.savedstate
public actual class SavedState
@PublishedApi
-internal constructor(@PublishedApi internal val map: MutableMap<String, Any> = mutableMapOf())
+internal constructor(@PublishedApi internal val map: MutableMap<String, Any?> = mutableMapOf())
-actual inline fun savedState(block: SavedStateWriter.() -> Unit): SavedState =
- SavedState().apply { write(block) }
+actual inline fun savedState(
+ initialState: Map<String, Any?>,
+ builderAction: SavedStateWriter.() -> Unit,
+): SavedState = SavedState(initialState.toMutableMap()).apply { write(builderAction) }
diff --git a/savedstate/savedstate/src/nonAndroidMain/kotlin/androidx/savedstate/SavedStateReader.nonAndroid.kt b/savedstate/savedstate/src/nonAndroidMain/kotlin/androidx/savedstate/SavedStateReader.nonAndroid.kt
index 01e3cd9..f3f1797 100644
--- a/savedstate/savedstate/src/nonAndroidMain/kotlin/androidx/savedstate/SavedStateReader.nonAndroid.kt
+++ b/savedstate/savedstate/src/nonAndroidMain/kotlin/androidx/savedstate/SavedStateReader.nonAndroid.kt
@@ -31,41 +31,37 @@
}
actual inline fun getBooleanOrElse(key: String, defaultValue: () -> Boolean): Boolean =
- getSingleResultOrElse(key, defaultValue) {
- source.map[key] as? Boolean ?: SavedStateUtils.DEFAULT_BOOLEAN
- }
+ getSingleResultOrElse(key, defaultValue) { source.map[key] as? Boolean }
+
+ actual inline fun getChar(key: String): Char =
+ getSingleResultOrThrow(key) { source.map[key] as? Char ?: SavedStateUtils.DEFAULT_CHAR }
+
+ actual inline fun getCharOrElse(key: String, defaultValue: () -> Char): Char =
+ getSingleResultOrElse(key, defaultValue) { source.map[key] as? Char }
actual inline fun getDouble(key: String): Double =
getSingleResultOrThrow(key) { source.map[key] as? Double ?: SavedStateUtils.DEFAULT_DOUBLE }
actual inline fun getDoubleOrElse(key: String, defaultValue: () -> Double): Double =
- getSingleResultOrElse(key, defaultValue) {
- source.map[key] as? Double ?: SavedStateUtils.DEFAULT_DOUBLE
- }
+ getSingleResultOrElse(key, defaultValue) { source.map[key] as? Double }
actual inline fun getFloat(key: String): Float =
getSingleResultOrThrow(key) { source.map[key] as? Float ?: SavedStateUtils.DEFAULT_FLOAT }
actual inline fun getFloatOrElse(key: String, defaultValue: () -> Float): Float =
- getSingleResultOrElse(key, defaultValue) {
- source.map[key] as? Float ?: SavedStateUtils.DEFAULT_FLOAT
- }
+ getSingleResultOrElse(key, defaultValue) { source.map[key] as? Float }
actual inline fun getInt(key: String): Int =
getSingleResultOrThrow(key) { source.map[key] as? Int ?: SavedStateUtils.DEFAULT_INT }
actual inline fun getIntOrElse(key: String, defaultValue: () -> Int): Int =
- getSingleResultOrElse(key, defaultValue) {
- source.map[key] as? Int ?: SavedStateUtils.DEFAULT_INT
- }
+ getSingleResultOrElse(key, defaultValue) { source.map[key] as? Int }
actual inline fun getLong(key: String): Long =
getSingleResultOrThrow(key) { source.map[key] as? Long ?: SavedStateUtils.DEFAULT_LONG }
actual inline fun getLongOrElse(key: String, defaultValue: () -> Long): Long =
- getSingleResultOrElse(key, defaultValue) {
- source.map[key] as? Long ?: SavedStateUtils.DEFAULT_LONG
- }
+ getSingleResultOrElse(key, defaultValue) { source.map[key] as? Long }
actual inline fun getString(key: String): String =
getSingleResultOrThrow(key) { source.map[key] as? String }
@@ -110,6 +106,10 @@
return source.map.isEmpty()
}
+ actual inline fun isNull(key: String): Boolean {
+ return contains(key) && source.map[key] == null
+ }
+
actual inline operator fun contains(key: String): Boolean {
return source.map.containsKey(key)
}
@@ -163,4 +163,9 @@
currentValue = { currentValue() },
defaultValue = { defaultValue() },
)
+
+ actual fun contentDeepEquals(other: SavedState): Boolean {
+ // Map implements `equals` as a content deep, there is no need to do anything else.
+ return source.map == other.map
+ }
}
diff --git a/savedstate/savedstate/src/nonAndroidMain/kotlin/androidx/savedstate/SavedStateWriter.nonAndroid.kt b/savedstate/savedstate/src/nonAndroidMain/kotlin/androidx/savedstate/SavedStateWriter.nonAndroid.kt
index 4b614d6..8d60484 100644
--- a/savedstate/savedstate/src/nonAndroidMain/kotlin/androidx/savedstate/SavedStateWriter.nonAndroid.kt
+++ b/savedstate/savedstate/src/nonAndroidMain/kotlin/androidx/savedstate/SavedStateWriter.nonAndroid.kt
@@ -26,6 +26,10 @@
source.map[key] = value
}
+ actual inline fun putChar(key: String, value: Char) {
+ source.map[key] = value
+ }
+
actual inline fun putDouble(key: String, value: Double) {
source.map[key] = value
}
@@ -42,6 +46,10 @@
source.map[key] = value
}
+ actual inline fun putNull(key: String) {
+ source.map[key] = null
+ }
+
actual inline fun putString(key: String, value: String) {
source.map[key] = value
}
diff --git a/security/security-state/src/androidTest/java/androidx/security/state/SecurityStateManagerTest.kt b/security/security-state/src/androidTest/java/androidx/security/state/SecurityStateManagerTest.kt
index 73796ac..b26e8b5 100644
--- a/security/security-state/src/androidTest/java/androidx/security/state/SecurityStateManagerTest.kt
+++ b/security/security-state/src/androidTest/java/androidx/security/state/SecurityStateManagerTest.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.os.Build
import android.os.Bundle
+import androidx.security.state.SecurityPatchState.DateBasedSecurityPatchLevel
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
@@ -140,4 +141,17 @@
val bundle = securityStateManager.getGlobalSecurityState()
assertFalse(bundle.containsKey("vendor_spl"))
}
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+ @Test
+ fun testGetGlobalSecurityState_withGoogleModules_doesNotThrow() {
+ if (!Build.MANUFACTURER.equals("Google", ignoreCase = true)) {
+ return // Skip this test on non-Google devices.
+ }
+ val bundle =
+ securityStateManager.getGlobalSecurityState("com.google.android.modulemetadata")
+ DateBasedSecurityPatchLevel.fromString(
+ bundle.getString("com.google.android.modulemetadata")!!
+ )
+ }
}
diff --git a/security/security-state/src/main/AndroidManifest.xml b/security/security-state/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4103b9d0
--- /dev/null
+++ b/security/security-state/src/main/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2024 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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <queries>
+ <package android:name="com.google.android.modulemetadata" />
+ <package android:name="com.google.mainline.telemetry" />
+ <package android:name="com.google.mainline.adservices" />
+ <package android:name="com.google.mainline.go.primary" />
+ <package android:name="com.google.mainline.go.telemetry" />
+ </queries>
+</manifest>
\ No newline at end of file
diff --git a/sqlite/integration-tests/driver-conformance-test/build.gradle b/sqlite/integration-tests/driver-conformance-test/build.gradle
index fd7b912..a2dc3f9 100644
--- a/sqlite/integration-tests/driver-conformance-test/build.gradle
+++ b/sqlite/integration-tests/driver-conformance-test/build.gradle
@@ -26,11 +26,20 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.sqlite.driver.test"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ }
ios()
jvm()
linux()
@@ -78,9 +87,6 @@
}
}
-android {
- namespace "androidx.sqlite.driver.test"
-}
androidx {
name = "SQLite Driver Coformance Base Tests"
diff --git a/sqlite/sqlite-framework/build.gradle b/sqlite/sqlite-framework/build.gradle
index 411b332f..c9fff30 100644
--- a/sqlite/sqlite-framework/build.gradle
+++ b/sqlite/sqlite-framework/build.gradle
@@ -29,10 +29,8 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
}
-
configurations {
// Configuration for resolving shared archive file of androidx's SQLite compilation
sqliteSharedArchive {
@@ -53,7 +51,17 @@
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.sqlite.db.framework"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ }
ios() {
// Link to sqlite3 available in iOS
binaries.configureEach {
@@ -148,6 +156,3 @@
description = "The implementation of SQLite library using the framework code."
}
-android {
- namespace "androidx.sqlite.db.framework"
-}
diff --git a/sqlite/sqlite/build.gradle b/sqlite/sqlite/build.gradle
index 3c4a438..43cf644 100644
--- a/sqlite/sqlite/build.gradle
+++ b/sqlite/sqlite/build.gradle
@@ -28,12 +28,20 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
}
-
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.sqlite.db"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ }
ios()
jvm()
linux()
@@ -81,9 +89,6 @@
}
}
-android {
- namespace "androidx.sqlite.db"
-}
androidx {
name = "SQLite"
diff --git a/testutils/testutils-lifecycle/build.gradle b/testutils/testutils-lifecycle/build.gradle
index 9d088a1..4408879 100644
--- a/testutils/testutils-lifecycle/build.gradle
+++ b/testutils/testutils-lifecycle/build.gradle
@@ -27,11 +27,20 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
}
androidXMultiplatform {
- android()
+ androidLibrary {
+ namespace = "androidx.testutils.lifecycle"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ withAndroidTestOnJvmBuilder {
+ it.defaultSourceSetName = "androidUnitTest"
+ }
+ }
desktop()
mac()
linux()
@@ -66,9 +75,6 @@
}
}
-android {
- namespace "androidx.testutils.lifecycle"
-}
androidx {
type = LibraryType.INTERNAL_TEST_LIBRARY
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/BasicSwipeToDismissBox.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/BasicSwipeToDismissBox.kt
index 78cdb6f..4ef9228 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/BasicSwipeToDismissBox.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/BasicSwipeToDismissBox.kt
@@ -351,14 +351,13 @@
edgeSwipeState: State<EdgeSwipeState>
): NestedScrollConnection =
object : NestedScrollConnection {
- @Suppress("DEPRECATION") // b/327155912
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val delta = available.x
// If swipeState = SwipeState.SWIPING_TO_DISMISS - perform swipeToDismiss
// drag and consume everything
return if (
edgeSwipeState.value == EdgeSwipeState.SwipingToDismiss &&
- source == NestedScrollSource.Drag
+ source == NestedScrollSource.UserInput
) {
dispatchRawDelta(delta)
available
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeableV2.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeableV2.kt
index c4ffe24..2f5c10d 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeableV2.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeableV2.kt
@@ -477,7 +477,6 @@
*
* @return The delta the consumed by the [SwipeableV2State]
*/
- @Suppress("DEPRECATION") // b/327155912
fun dispatchRawDelta(delta: Float): Float {
var remainingDelta = delta
@@ -486,7 +485,7 @@
val consumedByParent =
nestedScrollDispatcher.dispatchPreScroll(
available = offsetWithOrientation(remainingDelta),
- source = NestedScrollSource.Drag
+ source = NestedScrollSource.UserInput
)
remainingDelta -= (consumedByParent.x + consumedByParent.y)
}
@@ -503,7 +502,7 @@
nestedScrollDispatcher.dispatchPostScroll(
consumed = offsetWithOrientation(deltaToConsume),
available = offsetWithOrientation(delta - deltaToConsume),
- source = NestedScrollSource.Drag
+ source = NestedScrollSource.UserInput
)
remainingDelta -= (deltaToConsume + consumedDelta.x + consumedDelta.y)
}
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/SelectionControls.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/SelectionControls.kt
index aa1b486..988ad1c 100644
--- a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/SelectionControls.kt
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/SelectionControls.kt
@@ -20,6 +20,7 @@
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.CubicBezierEasing
+import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.Transition
import androidx.compose.animation.core.TweenSpec
import androidx.compose.animation.core.animateFloat
@@ -87,7 +88,7 @@
enabled: Boolean,
onCheckedChange: ((Boolean) -> Unit)?,
interactionSource: MutableInteractionSource?,
- progressAnimationSpec: TweenSpec<Float>,
+ progressAnimationSpec: FiniteAnimationSpec<Float>,
drawBox: FunctionDrawBox,
width: Dp,
height: Dp,
@@ -274,6 +275,59 @@
width: Dp,
height: Dp,
ripple: Indication
+) =
+ RadioButton(
+ modifier = modifier,
+ selected = selected,
+ enabled = enabled,
+ ringColor = ringColor,
+ dotColor = dotColor,
+ onClick = onClick,
+ interactionSource = interactionSource,
+ dotRadiusAnimationSpec = tween(dotRadiusProgressDuration(selected), 0, easing),
+ dotAlphaAnimationSpec = tween(dotAlphaProgressDuration, dotAlphaProgressDelay, easing),
+ width = width,
+ height = height,
+ ripple = ripple
+ )
+
+/**
+ * [RadioButton] provides an animated radio button for use in material APIs.
+ *
+ * @param modifier Modifier to be applied to the radio button. This can be used to provide a content
+ * description for accessibility.
+ * @param selected Boolean flag indicating whether this radio button is currently toggled on.
+ * @param enabled Boolean flag indicating the enabled state of the [RadioButton] (affects the
+ * color).
+ * @param ringColor Composable lambda from which the ring color of the radio button will be
+ * obtained.
+ * @param dotColor Composable lambda from which the dot color of the radio button will be obtained.
+ * @param onClick Callback to be invoked when RadioButton is clicked. If null, then this is passive
+ * and relies entirely on a higher-level component to control the state.
+ * @param interactionSource When also providing [onClick], the [MutableInteractionSource]
+ * representing the stream of [Interaction]s for the "toggleable" tap area - can be used to
+ * customise the appearance / behavior of the RadioButton.
+ * @param dotRadiusAnimationSpec Animation spec of the dot radius progress animation.
+ * @param dotAlphaAnimationSpec Animation spec of the dot alpha progress animation.
+ * @param width Width of the radio button.
+ * @param height Height of the radio button.
+ * @param ripple Ripple used for the radio button.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Composable
+fun RadioButton(
+ modifier: Modifier,
+ selected: Boolean,
+ enabled: Boolean,
+ ringColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color>,
+ dotColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color>,
+ onClick: (() -> Unit)?,
+ interactionSource: MutableInteractionSource?,
+ dotRadiusAnimationSpec: FiniteAnimationSpec<Float>,
+ dotAlphaAnimationSpec: FiniteAnimationSpec<Float>,
+ width: Dp,
+ height: Dp,
+ ripple: Indication
) {
val targetState = if (selected) SelectionStage.Checked else SelectionStage.Unchecked
val transition = updateTransition(targetState)
@@ -286,7 +340,7 @@
animateProgress(
transition = transition,
label = "dot-radius",
- animationSpec = tween(dotRadiusProgressDuration(selected), 0, easing)
+ animationSpec = dotRadiusAnimationSpec
)
// Animation of the dot alpha only happens when toggling On to Off.
val dotAlphaProgress =
@@ -294,7 +348,7 @@
animateProgress(
transition = transition,
label = "dot-alpha",
- animationSpec = tween(dotAlphaProgressDuration, dotAlphaProgressDelay, easing)
+ animationSpec = dotAlphaAnimationSpec
)
else null
@@ -426,7 +480,7 @@
private fun animateProgress(
transition: Transition<SelectionStage>,
label: String,
- animationSpec: TweenSpec<Float>,
+ animationSpec: FiniteAnimationSpec<Float>,
) =
transition.animateFloat(transitionSpec = { animationSpec }, label = label) {
when (it) {
diff --git a/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/dialog/DialogTest.kt b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/dialog/DialogTest.kt
index f28182d..a2e882f 100644
--- a/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/dialog/DialogTest.kt
+++ b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/dialog/DialogTest.kt
@@ -424,10 +424,11 @@
dismissCounter++
show.value = false
},
- durationMillis = 100
+ durationMillis = 300
)
}
}
+ rule.waitForIdle()
rule.waitUntilDoesNotExist(hasTestTag(TEST_TAG))
assertEquals(1, dismissCounter)
}
diff --git a/wear/compose/compose-material3/api/current.txt b/wear/compose/compose-material3/api/current.txt
index dc5e915..178be84 100644
--- a/wear/compose/compose-material3/api/current.txt
+++ b/wear/compose/compose-material3/api/current.txt
@@ -851,11 +851,11 @@
public final class PlaceholderDefaults {
method public androidx.compose.ui.graphics.Shape getShape();
- method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter painterWithPlaceholderOverlayBackgroundBrush(androidx.wear.compose.material3.PlaceholderState placeholderState, androidx.compose.ui.graphics.painter.Painter painter, optional long color);
+ method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter painterWithPlaceholderOverlayBackgroundBrush(androidx.wear.compose.material3.PlaceholderState placeholderState, androidx.compose.ui.graphics.painter.Painter originalPainter, optional long color);
method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter placeholderBackgroundBrush(androidx.wear.compose.material3.PlaceholderState placeholderState, optional long color);
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ButtonColors placeholderButtonColors(androidx.wear.compose.material3.ButtonColors originalButtonColors, androidx.wear.compose.material3.PlaceholderState placeholderState, optional long color);
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ButtonColors placeholderButtonColors(androidx.wear.compose.material3.PlaceholderState placeholderState, optional long color);
- property public final androidx.compose.ui.graphics.Shape shape;
+ property public final androidx.compose.ui.graphics.Shape Shape;
field public static final androidx.wear.compose.material3.PlaceholderDefaults INSTANCE;
}
@@ -866,13 +866,11 @@
}
@androidx.compose.runtime.Stable public final class PlaceholderState {
- method public float getPlaceholderProgression();
- method public boolean isShowContent();
- method public boolean isWipeOff();
- method public suspend Object? startPlaceholderAnimation(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final boolean isShowContent;
- property public final boolean isWipeOff;
- property public final float placeholderProgression;
+ method public suspend Object? animatePlaceholder(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public boolean isHidden();
+ method public boolean isWipingOff();
+ property public final boolean isHidden;
+ property public final boolean isWipingOff;
}
public final class ProgressIndicatorColors {
diff --git a/wear/compose/compose-material3/api/restricted_current.txt b/wear/compose/compose-material3/api/restricted_current.txt
index dc5e915..178be84 100644
--- a/wear/compose/compose-material3/api/restricted_current.txt
+++ b/wear/compose/compose-material3/api/restricted_current.txt
@@ -851,11 +851,11 @@
public final class PlaceholderDefaults {
method public androidx.compose.ui.graphics.Shape getShape();
- method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter painterWithPlaceholderOverlayBackgroundBrush(androidx.wear.compose.material3.PlaceholderState placeholderState, androidx.compose.ui.graphics.painter.Painter painter, optional long color);
+ method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter painterWithPlaceholderOverlayBackgroundBrush(androidx.wear.compose.material3.PlaceholderState placeholderState, androidx.compose.ui.graphics.painter.Painter originalPainter, optional long color);
method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter placeholderBackgroundBrush(androidx.wear.compose.material3.PlaceholderState placeholderState, optional long color);
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ButtonColors placeholderButtonColors(androidx.wear.compose.material3.ButtonColors originalButtonColors, androidx.wear.compose.material3.PlaceholderState placeholderState, optional long color);
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ButtonColors placeholderButtonColors(androidx.wear.compose.material3.PlaceholderState placeholderState, optional long color);
- property public final androidx.compose.ui.graphics.Shape shape;
+ property public final androidx.compose.ui.graphics.Shape Shape;
field public static final androidx.wear.compose.material3.PlaceholderDefaults INSTANCE;
}
@@ -866,13 +866,11 @@
}
@androidx.compose.runtime.Stable public final class PlaceholderState {
- method public float getPlaceholderProgression();
- method public boolean isShowContent();
- method public boolean isWipeOff();
- method public suspend Object? startPlaceholderAnimation(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final boolean isShowContent;
- property public final boolean isWipeOff;
- property public final float placeholderProgression;
+ method public suspend Object? animatePlaceholder(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public boolean isHidden();
+ method public boolean isWipingOff();
+ property public final boolean isHidden;
+ property public final boolean isWipingOff;
}
public final class ProgressIndicatorColors {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/DatePickerDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/DatePickerDemo.kt
index 9ea719e..621a163 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/DatePickerDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/DatePickerDemo.kt
@@ -29,6 +29,7 @@
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalConfiguration
import androidx.wear.compose.integration.demos.common.ComposableDemo
import androidx.wear.compose.material3.Button
import androidx.wear.compose.material3.DatePicker
@@ -40,6 +41,7 @@
import androidx.wear.compose.material3.samples.DatePickerYearMonthDaySample
import java.time.LocalDate
import java.time.format.DateTimeFormatter
+import java.time.format.FormatStyle
@RequiresApi(Build.VERSION_CODES.O)
val DatePickerDemos =
@@ -49,6 +51,7 @@
ComposableDemo("Date Day-Month-Year") { DatePickerDemo(DatePickerType.DayMonthYear) },
ComposableDemo("Date System date format") { DatePickerSample() },
ComposableDemo("Date Range") { DatePickerMinDateMaxDateSample() },
+ ComposableDemo("Past only") { DatePickerPastOnlyDemo() },
)
@RequiresApi(Build.VERSION_CODES.O)
@@ -85,3 +88,37 @@
}
}
}
+
+@RequiresApi(Build.VERSION_CODES.O)
+@Composable
+fun DatePickerPastOnlyDemo() {
+ val currentDate = LocalDate.now()
+ var showDatePicker by remember { mutableStateOf(true) }
+ var datePickerDate by remember { mutableStateOf(LocalDate.now()) }
+ val formatter =
+ DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
+ .withLocale(LocalConfiguration.current.locales[0])
+ if (showDatePicker) {
+ DatePicker(
+ initialDate = datePickerDate, // Initialize with last picked date on reopen
+ onDatePicked = {
+ datePickerDate = it
+ showDatePicker = false
+ },
+ datePickerType = DatePickerType.YearMonthDay,
+ maxDate = currentDate
+ )
+ } else {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center,
+ ) {
+ Button(
+ onClick = { showDatePicker = true },
+ label = { Text("Selected Date") },
+ secondaryLabel = { Text(datePickerDate.format(formatter)) },
+ icon = { Icon(imageVector = Icons.Filled.Edit, contentDescription = "Edit") },
+ )
+ }
+ }
+}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/PageIndicatorDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/PageIndicatorDemo.kt
new file mode 100644
index 0000000..b0ac510
--- /dev/null
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/PageIndicatorDemo.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2024 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.wear.compose.material3.demos
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.wear.compose.foundation.pager.HorizontalPager
+import androidx.wear.compose.foundation.pager.VerticalPager
+import androidx.wear.compose.foundation.pager.rememberPagerState
+import androidx.wear.compose.integration.demos.common.ComposableDemo
+import androidx.wear.compose.material3.HorizontalPageIndicator
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.VerticalPageIndicator
+import androidx.wear.compose.material3.samples.HorizontalPageIndicatorWithPagerSample
+import androidx.wear.compose.material3.samples.VerticalPageIndicatorWithPagerSample
+
+val PageIndicatorDemos =
+ listOf(
+ ComposableDemo("Horizontal PageIndicator") { HorizontalPageIndicatorWithPagerSample() },
+ ComposableDemo("Vertical PageIndicator") { VerticalPageIndicatorWithPagerSample() },
+ ComposableDemo("Vertical pager on left") { VerticalPageIndicatorWithPagerOnLeftSample() },
+ ComposableDemo("Horizontal with white background") {
+ HorizontalPageIndicatorWhiteBackgroundDemo()
+ },
+ )
+
+@Composable
+fun HorizontalPageIndicatorWhiteBackgroundDemo() {
+ val pageCount = 9
+ val pagerState = rememberPagerState { pageCount }
+
+ Box(modifier = Modifier.background(Color.White)) {
+ HorizontalPager(
+ state = pagerState,
+ ) { page ->
+ Box(modifier = Modifier.fillMaxSize()) {
+ Text(
+ modifier = Modifier.align(Alignment.Center),
+ text = "Page #$page",
+ color = Color.Black
+ )
+ }
+ }
+ HorizontalPageIndicator(pagerState = pagerState)
+ }
+}
+
+@Composable
+fun VerticalPageIndicatorWithPagerOnLeftSample() {
+ val pageCount = 9
+ val pagerState = rememberPagerState { pageCount }
+
+ CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+ Box {
+ VerticalPager(
+ state = pagerState,
+ ) { page ->
+ Box(modifier = Modifier.fillMaxSize()) {
+ Text(modifier = Modifier.align(Alignment.Center), text = "Page #$page")
+ }
+ }
+ VerticalPageIndicator(pagerState = pagerState)
+ }
+ }
+}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/PlaceholderDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/PlaceholderDemo.kt
index bf7184c..79493f9 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/PlaceholderDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/PlaceholderDemo.kt
@@ -405,7 +405,7 @@
placeholderState = buttonPlaceholderState
)
)
- if (!buttonPlaceholderState.isShowContent) {
+ if (!buttonPlaceholderState.isHidden) {
Button(
modifier =
modifier
@@ -457,7 +457,7 @@
)
}
}
- LaunchedEffect(buttonPlaceholderState) { buttonPlaceholderState.startPlaceholderAnimation() }
+ LaunchedEffect(buttonPlaceholderState) { buttonPlaceholderState.animatePlaceholder() }
}
@Composable
@@ -532,7 +532,7 @@
placeholderState = buttonPlaceholderState
)
)
- LaunchedEffect(buttonPlaceholderState) { buttonPlaceholderState.startPlaceholderAnimation() }
+ LaunchedEffect(buttonPlaceholderState) { buttonPlaceholderState.animatePlaceholder() }
}
@Composable
@@ -553,7 +553,7 @@
) {
if (content != null) content()
}
- if (!cardPlaceholderState.isShowContent) {
+ if (!cardPlaceholderState.isHidden) {
AppCard(
onClick = {},
appName = {
@@ -596,5 +596,5 @@
}
}
}
- LaunchedEffect(cardPlaceholderState) { cardPlaceholderState.startPlaceholderAnimation() }
+ LaunchedEffect(cardPlaceholderState) { cardPlaceholderState.animatePlaceholder() }
}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SwipeToRevealDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SwipeToRevealDemo.kt
index 4c33f50..e67c9f9 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SwipeToRevealDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SwipeToRevealDemo.kt
@@ -246,3 +246,29 @@
}
}
}
+
+@Composable
+fun SwipeToRevealSingleButtonWithAnchoring() {
+ SwipeToReveal(
+ revealState =
+ rememberRevealState(
+ swipeDirection = SwipeDirection.RightToLeft,
+ anchorWidth = SwipeToRevealDefaults.SingleActionAnchorWidth,
+ ),
+ actions = {
+ primaryAction(
+ onClick = { /* This block is called when the primary action is executed. */ },
+ icon = { Icon(Icons.Outlined.Delete, contentDescription = "Delete") },
+ label = "Delete"
+ )
+ undoPrimaryAction(
+ onClick = { /* This block is called when the undo primary action is executed. */ },
+ label = "Undo Delete"
+ )
+ }
+ ) {
+ Button(modifier = Modifier.fillMaxWidth(), onClick = {}) {
+ Text("This Button has only one action", modifier = Modifier.fillMaxSize())
+ }
+ }
+}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
index 40119b4..108dbdb 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
@@ -30,7 +30,6 @@
import androidx.wear.compose.material3.samples.EdgeButtonSample
import androidx.wear.compose.material3.samples.EdgeSwipeForSwipeToDismiss
import androidx.wear.compose.material3.samples.FixedFontSize
-import androidx.wear.compose.material3.samples.HorizontalPageIndicatorWithPagerSample
import androidx.wear.compose.material3.samples.HorizontalPagerScaffoldSample
import androidx.wear.compose.material3.samples.ScaffoldSample
import androidx.wear.compose.material3.samples.SimpleSwipeToDismissBox
@@ -40,7 +39,6 @@
import androidx.wear.compose.material3.samples.SwipeToRevealSingleActionCardSample
import androidx.wear.compose.material3.samples.TransformingLazyColumnScalingMorphingEffectSample
import androidx.wear.compose.material3.samples.TransformingLazyColumnTargetMorphingHeightSample
-import androidx.wear.compose.material3.samples.VerticalPageIndicatorWithPagerSample
import androidx.wear.compose.material3.samples.VerticalPagerScaffoldSample
val WearMaterial3Demos =
@@ -148,20 +146,13 @@
},
)
),
- Material3DemoCategory(
- title = "Page Indicator",
- listOf(
- ComposableDemo("HorizontalPageIndicator") {
- HorizontalPageIndicatorWithPagerSample()
- },
- ComposableDemo("VerticalPageIndicator") {
- VerticalPageIndicatorWithPagerSample()
- },
- )
- ),
+ Material3DemoCategory(title = "Page Indicator", PageIndicatorDemos),
Material3DemoCategory(
title = "Swipe to Reveal",
listOf(
+ ComposableDemo("Single Action with Anchoring") {
+ Centralize { SwipeToRevealSingleButtonWithAnchoring() }
+ },
ComposableDemo("Bi-directional / Non-anchoring") {
Centralize { SwipeToRevealBothDirectionsNonAnchoring() }
},
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/MacrobenchmarkScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/MacrobenchmarkScreen.kt
new file mode 100644
index 0000000..67330d5
--- /dev/null
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/MacrobenchmarkScreen.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 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.wear.compose.material3.macrobenchmark.common
+
+import androidx.benchmark.macro.MacrobenchmarkScope
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.runtime.Composable
+
+/** Represents a screen that can be used in Macrobenchmark tests. */
+interface MacrobenchmarkScreen {
+ val content: @Composable BoxScope.() -> Unit
+ val exercise: MacrobenchmarkScope.() -> Unit
+ get() = { device.waitForIdle() }
+}
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/TransformingLazyColumnBenchmark.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/TransformingLazyColumnBenchmark.kt
new file mode 100644
index 0000000..ccf678d
--- /dev/null
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/TransformingLazyColumnBenchmark.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2024 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.wear.compose.material3.macrobenchmark.common
+
+import android.graphics.Point
+import androidx.benchmark.macro.MacrobenchmarkScope
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.dp
+import androidx.test.uiautomator.By
+import androidx.wear.compose.foundation.lazy.TransformingLazyColumn
+import androidx.wear.compose.foundation.lazy.rememberTransformingLazyColumnState
+import androidx.wear.compose.material3.AppScaffold
+import androidx.wear.compose.material3.EdgeButton
+import androidx.wear.compose.material3.EdgeButtonSize
+import androidx.wear.compose.material3.MaterialTheme
+import androidx.wear.compose.material3.ScreenScaffold
+import androidx.wear.compose.material3.ScreenScaffoldDefaults
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.lazy.scrollTransform
+import kotlinx.coroutines.launch
+
+val TransformingLazyColumnBenchmark =
+ object : MacrobenchmarkScreen {
+ override val content: @Composable (BoxScope.() -> Unit)
+ get() = {
+ val state = rememberTransformingLazyColumnState()
+ val coroutineScope = rememberCoroutineScope()
+ AppScaffold {
+ ScreenScaffold(
+ state,
+ edgeButton = {
+ EdgeButton(
+ onClick = { coroutineScope.launch { state.scrollToItem(1) } }
+ ) {
+ Text("To top")
+ }
+ }
+ ) {
+ TransformingLazyColumn(
+ state = state,
+ contentPadding =
+ ScreenScaffoldDefaults.contentPaddingWithEdgeButton(
+ EdgeButtonSize.Small,
+ start = 10.dp,
+ end = 10.dp,
+ top = 20.dp,
+ extraBottom = 20.dp
+ ),
+ modifier =
+ Modifier.background(MaterialTheme.colorScheme.background)
+ .semantics { contentDescription = CONTENT_DESCRIPTION }
+ ) {
+ items(5000) {
+ Text(
+ "Item $it",
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.bodyLarge,
+ modifier =
+ Modifier.fillMaxWidth()
+ // Apply Material 3 Motion transformations.
+ .scrollTransform(
+ this,
+ backgroundColor =
+ MaterialTheme.colorScheme.surfaceContainer,
+ shape = MaterialTheme.shapes.small
+ )
+ .padding(10.dp)
+ )
+ }
+ }
+ }
+ }
+ }
+
+ override val exercise: MacrobenchmarkScope.() -> Unit
+ get() = {
+ val list = device.findObject(By.desc(CONTENT_DESCRIPTION))
+ // Setting a gesture margin is important otherwise gesture nav is triggered.
+ list.setGestureMargin(device.displayWidth / 5)
+ repeat(5) {
+ list.drag(Point(list.visibleCenter.x, list.visibleCenter.y / 3))
+ device.waitForIdle()
+ }
+ }
+ }
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/Utils.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/Utils.kt
index 1f54bb2..3cf03ed 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/Utils.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/Utils.kt
@@ -18,7 +18,9 @@
import androidx.test.uiautomator.UiDevice
-internal fun numberedContentDescription(n: Int) = "find-me-$n"
+internal val CONTENT_DESCRIPTION = "find-me"
+
+internal fun numberedContentDescription(n: Int) = "$CONTENT_DESCRIPTION-$n"
internal fun UiDevice.scrollDown() {
swipe(
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/AlertDialogScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/AlertDialogScreen.kt
index 2b5adb3..762b883 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/AlertDialogScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/AlertDialogScreen.kt
@@ -46,10 +46,11 @@
import androidx.wear.compose.material3.MaterialTheme
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.macrobenchmark.common.FIND_OBJECT_TIMEOUT_MS
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.macrobenchmark.common.R
val AlertDialogScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/AnimatedTextScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/AnimatedTextScreen.kt
index 6be0bfe..615f51c 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/AnimatedTextScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/AnimatedTextScreen.kt
@@ -49,12 +49,13 @@
import androidx.wear.compose.material3.AnimatedText
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.macrobenchmark.common.FIND_OBJECT_TIMEOUT_MS
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.macrobenchmark.common.R
import androidx.wear.compose.material3.rememberAnimatedTextFontRegistry
import kotlinx.coroutines.launch
val AnimatedTextScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/BaselineProfileScreens.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/BaselineProfileScreens.kt
index c50d169..e5b993a 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/BaselineProfileScreens.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/BaselineProfileScreens.kt
@@ -16,10 +16,6 @@
package androidx.wear.compose.material3.macrobenchmark.common.baselineprofile
-import androidx.benchmark.macro.MacrobenchmarkScope
-import androidx.compose.foundation.layout.BoxScope
-import androidx.compose.runtime.Composable
-
val BaselineProfileScreens =
listOf(
AlertDialogScreen,
@@ -56,10 +52,3 @@
TimePickerScreen,
TransformingLazyColumnScreen,
)
-
-/** Represents a screen used for generating a baseline profile. */
-interface BaselineProfileScreen {
- val content: @Composable BoxScope.() -> Unit
- val exercise: MacrobenchmarkScope.() -> Unit
- get() = { device.waitForIdle() }
-}
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ButtonGroupScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ButtonGroupScreen.kt
index 2125418..d7fc697 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ButtonGroupScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ButtonGroupScreen.kt
@@ -18,10 +18,11 @@
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.ButtonGroupSample
val ButtonGroupScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = { ButtonGroupSample() }
}
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ButtonScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ButtonScreen.kt
index 6274bc5..8766fc3 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ButtonScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ButtonScreen.kt
@@ -25,6 +25,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.wear.compose.material3.IconButtonDefaults
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.macrobenchmark.common.R
import androidx.wear.compose.material3.macrobenchmark.common.scrollDown
import androidx.wear.compose.material3.samples.ButtonSample
@@ -38,7 +39,7 @@
import androidx.wear.compose.material3.samples.OutlinedCompactButtonSample
val ButtonScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/CardScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/CardScreen.kt
index 4e149a6..7fbf379 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/CardScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/CardScreen.kt
@@ -37,11 +37,12 @@
import androidx.wear.compose.material3.OutlinedCard
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.TitleCard
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.macrobenchmark.common.R
import androidx.wear.compose.material3.macrobenchmark.common.scrollDown
val CardScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/CheckboxButtonScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/CheckboxButtonScreen.kt
index 8d8000a..69bf7b3 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/CheckboxButtonScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/CheckboxButtonScreen.kt
@@ -22,11 +22,12 @@
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.CheckboxButtonSample
import androidx.wear.compose.material3.samples.SplitCheckboxButtonSample
val CheckboxButtonScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ColorSchemeScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ColorSchemeScreen.kt
index f978ad6..a85413c 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ColorSchemeScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ColorSchemeScreen.kt
@@ -30,10 +30,11 @@
import androidx.wear.compose.material3.ButtonDefaults
import androidx.wear.compose.material3.MaterialTheme
import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.macrobenchmark.common.scrollDown
val ColorSchemeScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ConfirmationScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ConfirmationScreen.kt
index 8796147..406042b 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ConfirmationScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ConfirmationScreen.kt
@@ -26,10 +26,8 @@
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
@@ -45,11 +43,12 @@
import androidx.wear.compose.material3.SuccessConfirmation
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.macrobenchmark.common.FIND_OBJECT_TIMEOUT_MS
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.macrobenchmark.common.R
import androidx.wear.compose.material3.macrobenchmark.common.numberedContentDescription
val ConfirmationScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/CurvedTextScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/CurvedTextScreen.kt
index e5f6508..f149ecb 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/CurvedTextScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/CurvedTextScreen.kt
@@ -18,11 +18,12 @@
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.CurvedTextBottom
import androidx.wear.compose.material3.samples.CurvedTextTop
val CurvedTextScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
CurvedTextTop()
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/DatePickerScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/DatePickerScreen.kt
index 97b1296..a42a4a9 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/DatePickerScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/DatePickerScreen.kt
@@ -21,10 +21,11 @@
import androidx.compose.runtime.Composable
import androidx.wear.compose.material3.DatePicker
import androidx.wear.compose.material3.DatePickerType
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import java.time.LocalDate
val DatePickerScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
if (Build.VERSION.SDK_INT >= 26) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/EdgeButtonScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/EdgeButtonScreen.kt
index 910bac1..c2dc7ad 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/EdgeButtonScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/EdgeButtonScreen.kt
@@ -19,11 +19,12 @@
import androidx.benchmark.macro.MacrobenchmarkScope
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.macrobenchmark.common.scrollDown
import androidx.wear.compose.material3.samples.EdgeButtonSample
val EdgeButtonScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = { EdgeButtonSample() }
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/IconButtonScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/IconButtonScreen.kt
index 31d35a2..799e267 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/IconButtonScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/IconButtonScreen.kt
@@ -24,6 +24,7 @@
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.FilledIconButtonSample
import androidx.wear.compose.material3.samples.FilledTonalIconButtonSample
import androidx.wear.compose.material3.samples.FilledVariantIconButtonSample
@@ -34,7 +35,7 @@
@OptIn(ExperimentalLayoutApi::class)
val IconButtonScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable() (BoxScope.() -> Unit)
get() = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/IconToggleButtonScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/IconToggleButtonScreen.kt
index 2a170f3..d1b2bb1 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/IconToggleButtonScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/IconToggleButtonScreen.kt
@@ -36,13 +36,14 @@
import androidx.wear.compose.material3.IconToggleButton
import androidx.wear.compose.material3.IconToggleButtonDefaults
import androidx.wear.compose.material3.macrobenchmark.common.FIND_OBJECT_TIMEOUT_MS
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.macrobenchmark.common.R
import androidx.wear.compose.material3.samples.IconToggleButtonSample
import androidx.wear.compose.material3.samples.IconToggleButtonVariantSample
@OptIn(ExperimentalLayoutApi::class)
val IconToggleButtonScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable (BoxScope.() -> Unit)
get() = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ListHeaderScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ListHeaderScreen.kt
index 0cede8a..21a41e5 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ListHeaderScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ListHeaderScreen.kt
@@ -22,12 +22,13 @@
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.ListHeaderSample
import androidx.wear.compose.material3.samples.ListSubHeaderSample
import androidx.wear.compose.material3.samples.ListSubHeaderWithIconSample
val ListHeaderScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/OpenOnPhoneDialogScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/OpenOnPhoneDialogScreen.kt
index 1515b55..de4901b 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/OpenOnPhoneDialogScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/OpenOnPhoneDialogScreen.kt
@@ -35,9 +35,10 @@
import androidx.wear.compose.material3.FilledTonalButton
import androidx.wear.compose.material3.OpenOnPhoneDialog
import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
val OpenOnPhoneDialogScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
Column {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PageIndicatorScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PageIndicatorScreen.kt
index 1c069dc..c7c67f4 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PageIndicatorScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PageIndicatorScreen.kt
@@ -18,10 +18,11 @@
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.VerticalPageIndicatorWithPagerSample
val PageIndicatorScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = { VerticalPageIndicatorWithPagerSample() }
}
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PickerGroupScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PickerGroupScreen.kt
index 037263f..4a31a18 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PickerGroupScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PickerGroupScreen.kt
@@ -18,10 +18,11 @@
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.AutoCenteringPickerGroup
val PickerGroupScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable (BoxScope.() -> Unit)
get() = { AutoCenteringPickerGroup() }
}
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PickerScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PickerScreen.kt
index ba68003..3b5c299 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PickerScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PickerScreen.kt
@@ -18,10 +18,11 @@
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.PickerScrollToOption
val PickerScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = { PickerScrollToOption() }
}
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PlaceHolderScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PlaceHolderScreen.kt
index 1dd1def..5af8a5a 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PlaceHolderScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/PlaceHolderScreen.kt
@@ -22,12 +22,13 @@
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.ButtonWithIconAndLabelAndPlaceholders
import androidx.wear.compose.material3.samples.ButtonWithIconAndLabelsAndOverlaidPlaceholder
import androidx.wear.compose.material3.samples.TextPlaceholder
val PlaceHolderScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ProgressIndicatorScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ProgressIndicatorScreen.kt
index 8c3a78b..c9c5b8e 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ProgressIndicatorScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ProgressIndicatorScreen.kt
@@ -18,6 +18,7 @@
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.FullScreenProgressIndicatorSample
import androidx.wear.compose.material3.samples.IndeterminateProgressIndicatorSample
import androidx.wear.compose.material3.samples.MediaButtonProgressIndicatorSample
@@ -28,7 +29,7 @@
import androidx.wear.compose.material3.samples.SmallValuesProgressIndicatorSample
val ProgressIndicatorScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
FullScreenProgressIndicatorSample()
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/RadioButtonScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/RadioButtonScreen.kt
index db6b300..bff77db 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/RadioButtonScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/RadioButtonScreen.kt
@@ -22,11 +22,12 @@
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.RadioButtonSample
import androidx.wear.compose.material3.samples.SplitRadioButtonSample
val RadioButtonScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ScaffoldScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ScaffoldScreen.kt
index 8a63b05..462a87f 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ScaffoldScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ScaffoldScreen.kt
@@ -18,10 +18,11 @@
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.ScaffoldSample
val ScaffoldScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = { ScaffoldSample() }
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ScrollIndicatorScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ScrollIndicatorScreen.kt
index 3929d0e..088a27d 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ScrollIndicatorScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/ScrollIndicatorScreen.kt
@@ -18,10 +18,11 @@
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.ScrollIndicatorWithColumnSample
val ScrollIndicatorScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = { ScrollIndicatorWithColumnSample() }
}
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SliderScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SliderScreen.kt
index 5d133de..c622a31 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SliderScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SliderScreen.kt
@@ -23,6 +23,7 @@
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.macrobenchmark.common.scrollDown
import androidx.wear.compose.material3.samples.ChangedSliderSample
import androidx.wear.compose.material3.samples.SliderSample
@@ -30,7 +31,7 @@
import androidx.wear.compose.material3.samples.SliderWithIntegerSample
val SliderScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/StepperScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/StepperScreen.kt
index b262e91..6c89908 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/StepperScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/StepperScreen.kt
@@ -18,10 +18,11 @@
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.StepperWithButtonSample
val StepperScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = { StepperWithButtonSample() }
}
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SwipeToDismissScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SwipeToDismissScreen.kt
index 80fe730..482604e 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SwipeToDismissScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SwipeToDismissScreen.kt
@@ -18,10 +18,11 @@
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.StatefulSwipeToDismissBox
val SwipeToDismissScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = { StatefulSwipeToDismissBox() }
}
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SwipeToRevealScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SwipeToRevealScreen.kt
index 7356bc9..a940608 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SwipeToRevealScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SwipeToRevealScreen.kt
@@ -18,10 +18,11 @@
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.SwipeToRevealSample
val SwipeToRevealScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = { SwipeToRevealSample() }
}
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SwitchButtonScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SwitchButtonScreen.kt
index e9e98188..2e70194 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SwitchButtonScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/SwitchButtonScreen.kt
@@ -22,11 +22,12 @@
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.SplitSwitchButtonSample
import androidx.wear.compose.material3.samples.SwitchButtonSample
val SwitchButtonScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TextButtonScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TextButtonScreen.kt
index b9b82e7..e1d202e 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TextButtonScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TextButtonScreen.kt
@@ -24,6 +24,7 @@
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.FilledTextButtonSample
import androidx.wear.compose.material3.samples.FilledTonalTextButtonSample
import androidx.wear.compose.material3.samples.FilledVariantTextButtonSample
@@ -34,7 +35,7 @@
@OptIn(ExperimentalLayoutApi::class)
val TextButtonScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable (BoxScope.() -> Unit)
get() = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TextToggleButtonScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TextToggleButtonScreen.kt
index 72a2aa4..833143d 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TextToggleButtonScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TextToggleButtonScreen.kt
@@ -35,12 +35,13 @@
import androidx.wear.compose.material3.TextToggleButton
import androidx.wear.compose.material3.TextToggleButtonDefaults
import androidx.wear.compose.material3.macrobenchmark.common.FIND_OBJECT_TIMEOUT_MS
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.LargeTextToggleButtonSample
import androidx.wear.compose.material3.samples.TextToggleButtonSample
@OptIn(ExperimentalLayoutApi::class)
val TextToggleButtonScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable (BoxScope.() -> Unit)
get() = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TimePickerScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TimePickerScreen.kt
index 8022686..f506e50 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TimePickerScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TimePickerScreen.kt
@@ -18,10 +18,11 @@
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.TimePickerSample
val TimePickerScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = { TimePickerSample() }
}
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TimeTextScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TimeTextScreen.kt
index e2b5bb9..5565366 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TimeTextScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TimeTextScreen.kt
@@ -18,10 +18,11 @@
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.TimeTextWithStatus
val TimeTextScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = { TimeTextWithStatus() }
}
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TransformingLazyColumnScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TransformingLazyColumnScreen.kt
index ffb08e1..71ba2d3 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TransformingLazyColumnScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/TransformingLazyColumnScreen.kt
@@ -18,10 +18,11 @@
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.wear.compose.material3.macrobenchmark.common.MacrobenchmarkScreen
import androidx.wear.compose.material3.samples.TransformingLazyColumnScalingMorphingEffectSample
val TransformingLazyColumnScreen =
- object : BaselineProfileScreen {
+ object : MacrobenchmarkScreen {
override val content: @Composable BoxScope.() -> Unit
get() = { TransformingLazyColumnScalingMorphingEffectSample() }
}
diff --git a/wear/compose/compose-material3/macrobenchmark-target/src/main/AndroidManifest.xml b/wear/compose/compose-material3/macrobenchmark-target/src/main/AndroidManifest.xml
index 40010e2..9e2ee38 100644
--- a/wear/compose/compose-material3/macrobenchmark-target/src/main/AndroidManifest.xml
+++ b/wear/compose/compose-material3/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -43,6 +43,17 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
+
+ <activity
+ android:name=".TransformingLazyColumnActivity"
+ android:theme="@style/AppTheme"
+ android:exported="true">
+ <intent-filter>
+ <action android:name=
+ "androidx.wear.compose.material3.macrobenchmark.target.TRANSFORMING_LAZY_COLUMN_ACTIVITY" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
</application>
<uses-permission android:name="android.permission.WAKE_LOCK" />
diff --git a/wear/compose/compose-material3/macrobenchmark-target/src/main/java/androidx/wear/compose/material3/macrobenchmark/target/TransformingLazyColumnActivity.kt b/wear/compose/compose-material3/macrobenchmark-target/src/main/java/androidx/wear/compose/material3/macrobenchmark/target/TransformingLazyColumnActivity.kt
new file mode 100644
index 0000000..7bdd7eb
--- /dev/null
+++ b/wear/compose/compose-material3/macrobenchmark-target/src/main/java/androidx/wear/compose/material3/macrobenchmark/target/TransformingLazyColumnActivity.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024 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.wear.compose.material3.macrobenchmark.target
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.wear.compose.material3.MaterialTheme
+import androidx.wear.compose.material3.macrobenchmark.common.TransformingLazyColumnBenchmark
+
+class TransformingLazyColumnActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ MaterialTheme {
+ Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+ TransformingLazyColumnBenchmark.content.invoke(this)
+ }
+ }
+ }
+ }
+}
diff --git a/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/BaselineProfile.kt b/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/BaselineProfile.kt
index 168f053..254e765 100644
--- a/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/BaselineProfile.kt
+++ b/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/BaselineProfile.kt
@@ -78,7 +78,6 @@
}
companion object {
- private const val PACKAGE_NAME = "androidx.wear.compose.material3.macrobenchmark.target"
private const val BASELINE_ACTIVITY = "$PACKAGE_NAME.BASELINE_ACTIVITY"
}
}
diff --git a/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/Common.kt b/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/Common.kt
new file mode 100644
index 0000000..0a9b71d
--- /dev/null
+++ b/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/Common.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2024 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.wear.compose.material3.macrobenchmark
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+
+internal fun disableChargingExperience() {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val device = UiDevice.getInstance(instrumentation)
+ device.executeShellCommand(
+ "am broadcast -a " +
+ "com.google.android.clockwork.sysui.charging.ENABLE_CHARGING_EXPERIENCE " +
+ "--ez value \"false\" com.google.android.wearable.sysui"
+ )
+}
+
+internal fun enableChargingExperience() {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val device = UiDevice.getInstance(instrumentation)
+ device.executeShellCommand(
+ "am broadcast -a " +
+ "com.google.android.clockwork.sysui.charging.ENABLE_CHARGING_EXPERIENCE " +
+ "--ez value \"true\" com.google.android.wearable.sysui"
+ )
+}
+
+internal const val PACKAGE_NAME = "androidx.wear.compose.material3.macrobenchmark.target"
diff --git a/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/TransformingLazyColumnBenchmark.kt b/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/TransformingLazyColumnBenchmark.kt
new file mode 100644
index 0000000..1f82ab3
--- /dev/null
+++ b/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/TransformingLazyColumnBenchmark.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2024 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.wear.compose.material3.macrobenchmark
+
+import android.content.Intent
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.FrameTimingMetric
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.testutils.createCompilationParams
+import androidx.wear.compose.material3.macrobenchmark.common.TransformingLazyColumnBenchmark
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class TransformingLazyColumnBenchmark(private val compilationMode: CompilationMode) {
+ @get:Rule val benchmarkRule = MacrobenchmarkRule()
+
+ @Before
+ fun setUp() {
+ disableChargingExperience()
+ }
+
+ @After
+ fun destroy() {
+ enableChargingExperience()
+ }
+
+ @Test
+ fun start() {
+ benchmarkRule.measureRepeated(
+ packageName = PACKAGE_NAME,
+ metrics = listOf(FrameTimingMetric()),
+ compilationMode = compilationMode,
+ iterations = 10,
+ setupBlock = {
+ val intent = Intent()
+ intent.action = TRANSFORMING_LAZY_COLUMN_ACTIVITY
+ startActivityAndWait(intent)
+ }
+ ) {
+ TransformingLazyColumnBenchmark.exercise.invoke(this)
+ }
+ }
+
+ companion object {
+
+ private const val TRANSFORMING_LAZY_COLUMN_ACTIVITY =
+ "$PACKAGE_NAME.TRANSFORMING_LAZY_COLUMN_ACTIVITY"
+
+ @Parameterized.Parameters(name = "compilation={0}")
+ @JvmStatic
+ fun parameters() = createCompilationParams()
+ }
+}
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/PageIndicatorSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/PageIndicatorSample.kt
index 7ac6bd4..306f628 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/PageIndicatorSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/PageIndicatorSample.kt
@@ -22,7 +22,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
import androidx.wear.compose.foundation.pager.HorizontalPager
import androidx.wear.compose.foundation.pager.VerticalPager
import androidx.wear.compose.foundation.pager.rememberPagerState
@@ -32,7 +31,6 @@
@Sampled
@Composable
-@OptIn(ExperimentalWearFoundationApi::class)
fun HorizontalPageIndicatorWithPagerSample() {
val pageCount = 9
val pagerState = rememberPagerState { pageCount }
@@ -51,7 +49,6 @@
@Sampled
@Composable
-@OptIn(ExperimentalWearFoundationApi::class)
fun VerticalPageIndicatorWithPagerSample() {
val pageCount = 9
val pagerState = rememberPagerState { pageCount }
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/PlaceholderSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/PlaceholderSample.kt
index fedc367..50aa05f 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/PlaceholderSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/PlaceholderSample.kt
@@ -106,10 +106,8 @@
delay(1000)
labelText = "A label"
}
- if (!buttonPlaceholderState.isShowContent) {
- LaunchedEffect(buttonPlaceholderState) {
- buttonPlaceholderState.startPlaceholderAnimation()
- }
+ if (!buttonPlaceholderState.isHidden) {
+ LaunchedEffect(buttonPlaceholderState) { buttonPlaceholderState.animatePlaceholder() }
}
}
@@ -135,7 +133,7 @@
labelText.isNotEmpty() && secondaryLabelText.isNotEmpty() && imageVector != null
}
Box {
- if (buttonPlaceholderState.isShowContent || buttonPlaceholderState.isWipeOff) {
+ if (buttonPlaceholderState.isHidden || buttonPlaceholderState.isWipingOff) {
Button(
onClick = { /* Do something */ },
enabled = true,
@@ -171,7 +169,7 @@
modifier = Modifier.fillMaxWidth()
)
}
- if (!buttonPlaceholderState.isShowContent) {
+ if (!buttonPlaceholderState.isHidden) {
Button(
onClick = { /* Do something */ },
enabled = true,
@@ -220,10 +218,8 @@
delay(500)
labelText = "A label"
}
- if (!buttonPlaceholderState.isShowContent) {
- LaunchedEffect(buttonPlaceholderState) {
- buttonPlaceholderState.startPlaceholderAnimation()
- }
+ if (!buttonPlaceholderState.isHidden) {
+ LaunchedEffect(buttonPlaceholderState) { buttonPlaceholderState.animatePlaceholder() }
}
}
@@ -255,9 +251,7 @@
delay(3000)
labelText = "A label"
}
- if (!buttonPlaceholderState.isShowContent) {
- LaunchedEffect(buttonPlaceholderState) {
- buttonPlaceholderState.startPlaceholderAnimation()
- }
+ if (!buttonPlaceholderState.isHidden) {
+ LaunchedEffect(buttonPlaceholderState) { buttonPlaceholderState.animatePlaceholder() }
}
}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
index 5e3ffa6..e13753d 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
@@ -317,6 +317,7 @@
baseShape,
pressedShape,
0.75f,
+ 8,
color = { IconButtonDefaults.filledIconButtonColors().containerColor }
) { modifier ->
FilledIconButton(
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
index 6c5c90e..cee8cc6 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
@@ -23,10 +23,13 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.testutils.assertContainsColor
import androidx.compose.testutils.assertShape
import androidx.compose.ui.Modifier
@@ -621,6 +624,90 @@
}
@RequiresApi(Build.VERSION_CODES.O)
+ @Test
+ fun animates_corners_to_75_percent_on_click() {
+ val uncheckedShape = RoundedCornerShape(20.dp)
+ val checkedShape = RoundedCornerShape(10.dp)
+ val pressedShape = RoundedCornerShape(0.dp)
+ // Ignore the color transition from unchecked to checked color
+ val colors =
+ IconToggleButtonColors(
+ Color.Black,
+ Color.Black,
+ Color.Black,
+ Color.Black,
+ Color.Black,
+ Color.Black,
+ Color.Black,
+ Color.Black
+ )
+
+ rule.verifyRoundedButtonTapAnimationEnd(
+ uncheckedShape,
+ pressedShape,
+ 0.75f,
+ 8,
+ color = { colors.checkedContainerColor }
+ ) { modifier ->
+ IconToggleButton(
+ checked = false,
+ onCheckedChange = {},
+ modifier = modifier,
+ shapes = IconToggleButtonShapes(uncheckedShape, checkedShape, pressedShape),
+ colors = colors
+ ) {}
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ @Test
+ fun changes_unchecked_to_checked_shape_on_click() {
+ val uncheckedShape = RoundedCornerShape(20.dp)
+ val checkedShape = RoundedCornerShape(10.dp)
+ val pressedShape = RoundedCornerShape(0.dp)
+ rule.verifyRoundedButtonTapAnimationEnd(
+ uncheckedShape,
+ checkedShape,
+ 1f,
+ 100,
+ color = { IconToggleButtonDefaults.iconToggleButtonColors().checkedContainerColor },
+ antiAliasingGap = 4f,
+ ) { modifier ->
+ var checked by remember { mutableStateOf(false) }
+ IconToggleButton(
+ checked = checked,
+ onCheckedChange = { checked = !checked },
+ modifier = modifier,
+ shapes = IconToggleButtonShapes(uncheckedShape, checkedShape, pressedShape)
+ ) {}
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ @Test
+ fun changes_checked_to_unchecked_shape_on_click() {
+ val uncheckedShape = RoundedCornerShape(10.dp)
+ val checkedShape = RoundedCornerShape(20.dp)
+ val pressedShape = RoundedCornerShape(0.dp)
+ rule.verifyRoundedButtonTapAnimationEnd(
+ checkedShape,
+ uncheckedShape,
+ 1f,
+ 100,
+ color = { IconToggleButtonDefaults.iconToggleButtonColors().uncheckedContainerColor },
+ antiAliasingGap = 4f,
+ ) { modifier ->
+ var checked by remember { mutableStateOf(true) }
+ IconToggleButton(
+ checked = checked,
+ onCheckedChange = { checked = !checked },
+ modifier = modifier,
+ shapes = IconToggleButtonShapes(uncheckedShape, checkedShape, pressedShape)
+ ) {}
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
private fun ComposeContentTestRule.verifyIconToggleButtonColors(
status: Status,
checked: Boolean,
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt
index f383b29..adb74f9 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt
@@ -403,7 +403,9 @@
baseShape: RoundedCornerShape,
pressedShape: RoundedCornerShape,
targetProgress: Float,
+ expectedFramesUntilTarget: Int,
color: @Composable () -> Color,
+ antiAliasingGap: Float = 2f,
content: @Composable (Modifier) -> Unit
) {
val expectedAnimationEnd =
@@ -426,7 +428,7 @@
* 2) rule.mainClock.waitUntil expects a condition. However, the shape validations for
* ImageBitMap only includes of assets
*/
- repeat(8) { mainClock.advanceTimeByFrame() }
+ repeat(expectedFramesUntilTarget) { mainClock.advanceTimeByFrame() }
onNodeWithTag(TEST_TAG)
.captureToImage()
@@ -436,7 +438,7 @@
verticalPadding = 0.dp,
shapeColor = fillColor,
backgroundColor = Color.Transparent,
- antiAliasingGap = 2.0f,
+ antiAliasingGap = antiAliasingGap,
shape = expectedAnimationEnd,
)
}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/PlaceholderTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/PlaceholderTest.kt
index 9383e90..3234616 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/PlaceholderTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/PlaceholderTest.kt
@@ -57,12 +57,12 @@
}
// For testing we need to manually manage the frame clock for the placeholder animation
- placeholderState.initializeTestFrameMillis(PlaceholderStage.ShowContent)
+ placeholderState.initializeTestFrameMillis(PlaceholderStage.HidePlaceholder)
// Advance placeholder clock without changing the content ready and confirm still in
// ShowPlaceholder
placeholderState.advanceToNextPlaceholderAnimationLoopAndCheckStage(
- PlaceholderStage.ShowContent
+ PlaceholderStage.HidePlaceholder
)
}
@@ -92,7 +92,7 @@
// Advance the clock by one cycle and check we have moved to ShowContent
placeholderState.advanceFrameMillisAndCheckState(
PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS,
- PlaceholderStage.ShowContent
+ PlaceholderStage.HidePlaceholder
)
}
@@ -112,12 +112,12 @@
}
// For testing we need to manually manage the frame clock for the placeholder animation
- placeholderState.initializeTestFrameMillis(PlaceholderStage.ShowContent)
+ placeholderState.initializeTestFrameMillis(PlaceholderStage.HidePlaceholder)
// Advance placeholder clock without changing the content ready and confirm still in
// ShowPlaceholder
placeholderState.advanceToNextPlaceholderAnimationLoopAndCheckStage(
- PlaceholderStage.ShowContent
+ PlaceholderStage.HidePlaceholder
)
contentReady.value = false
@@ -182,7 +182,7 @@
// Advance the clock by one cycle and check we have moved to ShowContent
placeholderState.advanceFrameMillisAndCheckState(
PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS,
- PlaceholderStage.ShowContent
+ PlaceholderStage.HidePlaceholder
)
rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedBackgroundColor)
@@ -240,7 +240,7 @@
// Advance the clock by one cycle and check we have moved to ShowContent
placeholderState.advanceFrameMillisAndCheckState(
PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS,
- PlaceholderStage.ShowContent
+ PlaceholderStage.HidePlaceholder
)
// Check that the shimmer is no longer visible
@@ -317,7 +317,7 @@
// Now move the end of the wipe-off and confirm that the proper button background is visible
placeholderState.advanceFrameMillisAndCheckState(
PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS,
- PlaceholderStage.ShowContent
+ PlaceholderStage.HidePlaceholder
)
// Check that normal button background is now visible
@@ -338,7 +338,7 @@
placeholderState = placeholderState,
),
)
- LaunchedEffect(placeholderState) { placeholderState.startPlaceholderAnimation() }
+ LaunchedEffect(placeholderState) { placeholderState.animatePlaceholder() }
}
@Test
@@ -366,7 +366,7 @@
placeholderState.value?.advanceFrameMillisAndCheckState(
PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS,
- PlaceholderStage.ShowContent
+ PlaceholderStage.HidePlaceholder
)
}
@@ -392,7 +392,7 @@
placeholderState = placeholderState,
),
)
- LaunchedEffect(placeholderState) { placeholderState.startPlaceholderAnimation() }
+ LaunchedEffect(placeholderState) { placeholderState.animatePlaceholder() }
}
placeholderState.initializeTestFrameMillis()
@@ -408,7 +408,7 @@
placeholderState.advanceFrameMillisAndCheckState(
PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS,
- PlaceholderStage.ShowContent
+ PlaceholderStage.HidePlaceholder
)
// Check the placeholder background has gone and that we can see the buttons background
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
index 202c8b9..3a64620 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
@@ -536,6 +536,7 @@
baseShape,
pressedShape,
0.75f,
+ 8,
color = { TextButtonDefaults.filledTextButtonColors().containerColor }
) { modifier ->
TextButton(
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
index 1455f333..2b3d35e 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
@@ -23,10 +23,13 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.testutils.assertContainsColor
import androidx.compose.testutils.assertShape
import androidx.compose.ui.Modifier
@@ -604,6 +607,90 @@
.assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, overrideRole))
}
+ @RequiresApi(Build.VERSION_CODES.O)
+ @Test
+ fun animates_corners_to_75_percent_on_click() {
+ val uncheckedShape = RoundedCornerShape(20.dp)
+ val checkedShape = RoundedCornerShape(10.dp)
+ val pressedShape = RoundedCornerShape(0.dp)
+ // Ignore the color transition from unchecked to checked color
+ val colors =
+ TextToggleButtonColors(
+ Color.Black,
+ Color.Black,
+ Color.Black,
+ Color.Black,
+ Color.Black,
+ Color.Black,
+ Color.Black,
+ Color.Black
+ )
+
+ rule.verifyRoundedButtonTapAnimationEnd(
+ uncheckedShape,
+ pressedShape,
+ 0.75f,
+ 8,
+ color = { colors.checkedContainerColor }
+ ) { modifier ->
+ TextToggleButton(
+ checked = false,
+ onCheckedChange = {},
+ modifier = modifier,
+ shapes = TextToggleButtonShapes(uncheckedShape, checkedShape, pressedShape),
+ colors = colors
+ ) {}
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ @Test
+ fun changes_unchecked_to_checked_shape_on_click() {
+ val uncheckedShape = RoundedCornerShape(20.dp)
+ val checkedShape = RoundedCornerShape(10.dp)
+ val pressedShape = RoundedCornerShape(0.dp)
+ rule.verifyRoundedButtonTapAnimationEnd(
+ uncheckedShape,
+ checkedShape,
+ 1f,
+ 100,
+ color = { TextToggleButtonDefaults.textToggleButtonColors().checkedContainerColor },
+ antiAliasingGap = 4f,
+ ) { modifier ->
+ var checked by remember { mutableStateOf(false) }
+ TextToggleButton(
+ checked = checked,
+ onCheckedChange = { checked = !checked },
+ modifier = modifier,
+ shapes = TextToggleButtonShapes(uncheckedShape, checkedShape, pressedShape)
+ ) {}
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ @Test
+ fun changes_checked_to_unchecked_shape_on_click() {
+ val uncheckedShape = RoundedCornerShape(10.dp)
+ val checkedShape = RoundedCornerShape(20.dp)
+ val pressedShape = RoundedCornerShape(0.dp)
+ rule.verifyRoundedButtonTapAnimationEnd(
+ checkedShape,
+ uncheckedShape,
+ 1f,
+ 100,
+ color = { TextToggleButtonDefaults.textToggleButtonColors().uncheckedContainerColor },
+ antiAliasingGap = 4f,
+ ) { modifier ->
+ var checked by remember { mutableStateOf(true) }
+ TextToggleButton(
+ checked = checked,
+ onCheckedChange = { checked = !checked },
+ modifier = modifier,
+ shapes = TextToggleButtonShapes(uncheckedShape, checkedShape, pressedShape)
+ ) {}
+ }
+ }
+
@Composable
private fun shapeColor(): Color {
return TextToggleButtonDefaults.textToggleButtonColors()
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AnimatedToggleRoundedCornerShape.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AnimatedToggleRoundedCornerShape.kt
index f660f8f..a4a8ebc 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AnimatedToggleRoundedCornerShape.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AnimatedToggleRoundedCornerShape.kt
@@ -16,49 +16,53 @@
package androidx.wear.compose.material3
+import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.FiniteAnimationSpec
-import androidx.compose.animation.core.animateFloat
-import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.shape.CornerSize
-import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.geometry.RoundRect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.geometry.toRect
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
+import kotlinx.coroutines.launch
/**
- * A animated [RoundedCornerShape]. Animation is driven by changes to the [cornerSize] lambda.
- * [currentShapeSize] is provided as Size is received here, but must affect the animation.
+ * An implementation similar to RoundedCornerShape, but based on linear interpolation between a
+ * start and stop CornerSize, and an observable progress between 0.0 and 1.0.
*
- * @param currentShapeSize MutableState coordinating the current size.
- * @param cornerSize a lambda resolving to the current Corner size.
+ * @param startCornerSize the corner size when progress is 0.0
+ * @param endCornerSize the corner size when progress is 1.0
+ * @param progress returns the current progress from start to stop.
*/
@Stable
-internal class AnimatedToggleRoundedCornerShape(
- private val currentShapeSize: MutableState<Size?>,
- private val cornerSize: () -> CornerSize,
+private class AnimatedToggleRoundedCornerShape(
+ var startCornerSize: CornerSize,
+ var endCornerSize: CornerSize,
+ var progress: () -> Float,
) : Shape {
override fun createOutline(
size: Size,
layoutDirection: LayoutDirection,
density: Density,
): Outline {
- val cornerRadius = cornerSize().toPx(size, density)
-
- currentShapeSize.value = size
+ val animatedCornerSize = AnimatedCornerSize(startCornerSize, endCornerSize, progress)
+ val animatedCornerSizePx = animatedCornerSize.toPx(size, density)
return Outline.Rounded(
roundRect =
- RoundRect(rect = size.toRect(), radiusX = cornerRadius, radiusY = cornerRadius)
+ RoundRect(
+ rect = size.toRect(),
+ radiusX = animatedCornerSizePx,
+ radiusY = animatedCornerSizePx
+ )
)
}
}
@@ -84,49 +88,41 @@
else -> ToggleState.Unchecked
}
- val transition = updateTransition(toggleState, label = "Toggle State")
- val density = LocalDensity.current
+ val previous = remember { mutableStateOf(toggleState) }
+ val scope = rememberCoroutineScope()
+ val progress = remember { Animatable(1f) }
- val currentShapeSize = remember { mutableStateOf<Size?>(null) }
+ val toggledCornerSize =
+ toggleState.cornerSize(uncheckedCornerSize, checkedCornerSize, pressedCornerSize)
+ val animationSpec = if (pressed) onPressAnimationSpec else onReleaseAnimationSpec
- val observedSize = currentShapeSize.value
+ val animatedShape = remember {
+ AnimatedToggleRoundedCornerShape(
+ startCornerSize = toggledCornerSize,
+ endCornerSize = toggledCornerSize,
+ progress = { progress.value },
+ )
+ }
- if (observedSize != null) {
- val sizePx =
- transition.animateFloat(
- label = "Corner Size",
- transitionSpec = {
- when {
- targetState isTransitioningTo ToggleState.Pressed -> onPressAnimationSpec
- else -> onReleaseAnimationSpec
- }
- },
- ) { newState ->
- newState
- .cornerSize(uncheckedCornerSize, checkedCornerSize, pressedCornerSize)
- .toPx(observedSize, density)
- }
-
- return remember(sizePx) {
- AnimatedToggleRoundedCornerShape(
- currentShapeSize = currentShapeSize,
- ) {
- CornerSize(sizePx.value)
- }
+ LaunchedEffect(toggleState) {
+ // Allow the press up animation to finish its minimum duration before starting the next
+ if (!pressed) {
+ waitUntil { !progress.isRunning || progress.value > MIN_REQUIRED_ANIMATION_PROGRESS }
}
- } else {
- return remember(toggleState, uncheckedCornerSize, checkedCornerSize, pressedCornerSize) {
- AnimatedToggleRoundedCornerShape(
- currentShapeSize = currentShapeSize,
- ) {
- toggleState.cornerSize(
- uncheckedCornerSize,
- checkedCornerSize,
- pressedCornerSize,
- )
+
+ if (toggleState != previous.value) {
+ animatedShape.startCornerSize = animatedShape.endCornerSize
+ animatedShape.endCornerSize = toggledCornerSize
+ previous.value = toggleState
+
+ scope.launch {
+ progress.snapTo(1f - progress.value)
+ progress.animateTo(1f, animationSpec = animationSpec)
}
}
}
+
+ return animatedShape
}
private fun ToggleState.cornerSize(
@@ -145,3 +141,5 @@
Checked,
Pressed,
}
+
+private const val MIN_REQUIRED_ANIMATION_PROGRESS = 0.75f
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AnimationSpecUtils.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AnimationSpecUtils.kt
index 11dbdef..f28dd95 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AnimationSpecUtils.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AnimationSpecUtils.kt
@@ -26,6 +26,7 @@
import androidx.compose.animation.core.SpringSpec
import androidx.compose.animation.core.TwoWayConverter
import androidx.compose.animation.core.VectorizedAnimationSpec
+import androidx.compose.runtime.withFrameMillis
/**
* Returns a new [AnimationSpec] that is a faster version of this one.
@@ -129,3 +130,14 @@
}
as T
}
+
+internal suspend fun waitUntil(condition: () -> Boolean) {
+ val initialTimeMillis = withFrameMillis { it }
+ while (!condition()) {
+ val timeMillis = withFrameMillis { it }
+ if (timeMillis - initialTimeMillis > MAX_WAIT_TIME_MILLIS) return
+ }
+ return
+}
+
+private const val MAX_WAIT_TIME_MILLIS = 1_000L
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/CheckboxButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/CheckboxButton.kt
index 69996bc..86d0df6 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/CheckboxButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/CheckboxButton.kt
@@ -17,8 +17,7 @@
package androidx.wear.compose.material3
import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.TweenSpec
-import androidx.compose.animation.core.tween
+import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.Interaction
@@ -63,7 +62,6 @@
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material3.tokens.CheckboxButtonTokens
-import androidx.wear.compose.material3.tokens.MotionTokens
import androidx.wear.compose.material3.tokens.ShapeTokens
import androidx.wear.compose.material3.tokens.SplitCheckboxButtonTokens
import androidx.wear.compose.materialcore.animateSelectionColor
@@ -1526,7 +1524,7 @@
private val SPLIT_MIN_WIDTH = 48.dp
private val SPLIT_SECTIONS_SHAPE = ShapeTokens.CornerExtraSmall
-private val COLOR_ANIMATION_SPEC: AnimationSpec<Color> =
- tween(MotionTokens.DurationMedium1, 0, MotionTokens.EasingStandardDecelerate)
-private val PROGRESS_ANIMATION_SPEC: TweenSpec<Float> =
- tween(MotionTokens.DurationMedium1, 0, MotionTokens.EasingStandardDecelerate)
+private val COLOR_ANIMATION_SPEC: AnimationSpec<Color>
+ @Composable get() = MaterialTheme.motionScheme.slowEffectsSpec()
+private val PROGRESS_ANIMATION_SPEC: FiniteAnimationSpec<Float>
+ @Composable get() = MaterialTheme.motionScheme.fastEffectsSpec()
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Placeholder.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Placeholder.kt
index 8eef2be..cca2673 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Placeholder.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Placeholder.kt
@@ -100,7 +100,7 @@
@Composable
fun Modifier.placeholder(
placeholderState: PlaceholderState,
- shape: Shape = PlaceholderDefaults.shape,
+ shape: Shape = PlaceholderDefaults.Shape,
color: Color =
MaterialTheme.colorScheme.onSurface
.copy(alpha = 0.1f)
@@ -152,7 +152,7 @@
@Composable
fun Modifier.placeholderShimmer(
placeholderState: PlaceholderState,
- shape: Shape = PlaceholderDefaults.shape,
+ shape: Shape = PlaceholderDefaults.Shape,
color: Color = MaterialTheme.colorScheme.onSurface,
): Modifier =
this.then(
@@ -175,8 +175,8 @@
)
/**
- * Creates a [PlaceholderState] that is remembered across compositions. To start placeholder
- * animations run [PlaceholderState.startPlaceholderAnimation].
+ * Creates a [PlaceholderState] that is remembered across compositions. To animate the placeholder
+ * depending on the state, run [PlaceholderState.animatePlaceholder].
*
* A [PlaceholderState] should be created for each component that has placeholder data. The state is
* used to coordinate all of the different placeholder effects and animations.
@@ -222,15 +222,15 @@
object PlaceholderDefaults {
/** Default [Shape] for Placeholder. */
- val shape: Shape = ShapeTokens.CornerFull
+ val Shape: Shape = ShapeTokens.CornerFull
/**
* Create a [ButtonColors] that can be used in placeholder mode. This will provide the
* placeholder background effect that covers the normal button background with a solid
* background of [color] when the [placeholderState] is set to show the placeholder and a wipe
* off gradient brush when the state is in wipe-off mode. If the state is
- * [PlaceholderState.isShowContent] then the normal background will be used. All other colors
- * will be delegated to [originalButtonColors].
+ * [PlaceholderState.isHidden] then the normal background will be used. All other colors will be
+ * delegated to [originalButtonColors].
*
* Example of a [Button] with icon and a label that put placeholders over individual content
* slots and then draws a placeholder shimmer over the result and draws over the [Button]s
@@ -248,7 +248,7 @@
placeholderState: PlaceholderState,
color: Color = MaterialTheme.colorScheme.surfaceContainer
): ButtonColors {
- return if (!placeholderState.isShowContent) {
+ return if (!placeholderState.isHidden) {
ButtonColors(
containerPainter =
PlaceholderBackgroundPainter(
@@ -318,37 +318,37 @@
/**
* Create a [Painter] that wraps another painter and overlays a placeholder background brush on
- * top. If the [placeholderState] is [PlaceholderState.isShowContent] the original painter will
- * be used. Otherwise the [painter] will be drawn and then a placeholder background will be
+ * top. If the [placeholderState] is [PlaceholderState.isHidden] the original painter will be
+ * used. Otherwise the [originalPainter] will be drawn and then a placeholder background will be
* drawn over it or a wipe-off brush will be used to reveal the background when the state is
- * [PlaceholderState.isWipeOff].
+ * [PlaceholderState.isWipingOff].
*
* @param placeholderState the state of the placeholder
- * @param painter the original painter that will be drawn over when in placeholder mode.
+ * @param originalPainter the original painter that will be drawn over when in placeholder mode.
* @param color the color to use for the placeholder background brush
*/
@Composable
fun painterWithPlaceholderOverlayBackgroundBrush(
placeholderState: PlaceholderState,
- painter: Painter,
+ originalPainter: Painter,
color: Color = MaterialTheme.colorScheme.surfaceContainer,
): Painter {
- return if (!placeholderState.isShowContent) {
+ return if (!placeholderState.isHidden) {
PlaceholderBackgroundPainter(
- painter = painter,
+ painter = originalPainter,
placeholderState = placeholderState,
color = color
)
} else {
- painter
+ originalPainter
}
}
/**
* Create a [Painter] that paints with a placeholder background brush. If the [placeholderState]
- * is [PlaceholderState.isShowContent] then a transparent background will be shown. Otherwise a
+ * is [PlaceholderState.isHidden] then a transparent background will be shown. Otherwise a
* placeholder background will be drawn or a wipe-off brush will be used to reveal the content
- * underneath when [PlaceholderState.isWipeOff] is true.
+ * underneath when [PlaceholderState.isWipingOff] is true.
*
* @param placeholderState the state of the placeholder
* @param color the color to use for the placeholder background brush
@@ -418,8 +418,11 @@
*/
internal var backgroundOffset: Offset = Offset.Zero
- /** Start the animation of the placeholder state. */
- suspend fun startPlaceholderAnimation() {
+ /**
+ * Animate the placeholder according to the placeholder state. Cancels when the placeholder
+ * becomes inactive.
+ */
+ suspend fun animatePlaceholder() {
if (!isReduceMotionEnabled) {
coroutineScope {
while (isActive) {
@@ -430,7 +433,7 @@
}
/**
- * The current value of the placeholder wipe-off visual effect gradient progression. The
+ * The current value of the placeholder wipe-off visual effect gradient progression in Px. The
* progression is a 45 degree angle sweep across the whole screen running from outside of the
* Top|Left of the screen to Bottom|Right used as the anchor for wipe-off gradient effects.
*
@@ -439,8 +442,7 @@
* of height/width to create a 45 degree angle) * 1.75f and progress to the
* maximumScreenDimension * 0.75f.
*
- * The time taken for this progression to reach the edge of visible screen is
- * [PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS]
+ * The time taken for this progression to reach the edge of visible screen is 300ms.
*/
internal val placeholderWipeOffProgression: Float by derivedStateOf {
val absoluteProgression =
@@ -461,7 +463,7 @@
* gradient that flows across the screen. The progression will start at -maxScreenDimension (max
* of height/width to create a 45 degree angle) and progress to the maximumScreenDimension.
*
- * The time taken for this progression is [PLACEHOLDER_WIPE_OFF_PROGRESSION_ALPHA_DURATION_MS]
+ * The time taken for this progression is 80ms.
*/
internal val placeholderWipeOffAlpha: Float by derivedStateOf {
val absoluteProgression =
@@ -475,12 +477,12 @@
}
/**
- * The current value of the placeholder visual effect gradient progression. The progression
- * gives the x coordinate to be applied to the placeholder gradient as it moves across the
- * screen. Starting off screen to the left and progressing across the screen and finishing off
- * the screen to the right after [PLACEHOLDER_SHIMMER_DURATION_MS].
+ * The current value of the placeholder visual effect gradient progression in Px. The
+ * progression gives the x coordinate to be applied to the placeholder gradient as it moves
+ * across the screen. Starting off screen to the left and progressing across the screen and
+ * finishing off the screen to the after 800ms.
*/
- val placeholderProgression: Float by derivedStateOf {
+ internal val placeholderShimmerProgression: Float by derivedStateOf {
val absoluteProgression =
(frameMillis.longValue
.mod(PLACEHOLDER_SHIMMER_GAP_BETWEEN_ANIMATION_LOOPS_MS)
@@ -493,7 +495,7 @@
/**
* The current value of the placeholder visual effect gradient progression alpha/opacity. The
* progression gives the alpha to apply during the period of the placeholder effect. This allows
- * the effect to be faded in and then out during the [PLACEHOLDER_SHIMMER_DURATION_MS].
+ * the effect to be faded in and then out during 800ms.
*/
internal val placeholderShimmerAlpha: Float by derivedStateOf {
val absoluteProgression =
@@ -514,7 +516,7 @@
/**
* The current value of the placeholder visual effect gradient progression alpha/opacity during
* the fade-in part of reset placeholder animation. This allows the effect to be faded in during
- * the [PLACEHOLDER_RESET_ANIMATION_DURATION_MS].
+ * 450ms.
*/
internal val resetPlaceholderFadeInAlpha: Float by derivedStateOf {
val absoluteProgression =
@@ -534,7 +536,7 @@
/**
* The current value of the placeholder visual effect gradient progression alpha/opacity during
* the fade-out part of reset placeholder animation. This allows the effect to be faded out
- * during the [PLACEHOLDER_RESET_ANIMATION_DURATION_MS].
+ * during 450ms.
*/
internal val resetPlaceholderFadeOutAlpha: Float by derivedStateOf {
val absoluteProgression =
@@ -549,15 +551,13 @@
* Returns true if the placeholder content should be shown with no placeholders effects and
* false if either the placeholder or the wipe-off effect are being shown.
*/
- val isShowContent: Boolean by derivedStateOf {
- placeholderStage == PlaceholderStage.ShowContent
- }
+ val isHidden: Boolean by derivedStateOf { placeholderStage == PlaceholderStage.HidePlaceholder }
/**
- * Should only be called when [isShowContent] is false. Returns true if the wipe-off effect that
- * reveals content should be shown and false if the placeholder effect should be shown.
+ * Returns true if the wipe-off effect that reveals content is being shown and false if the
+ * placeholder effect should be shown.
*/
- val isWipeOff: Boolean by derivedStateOf { placeholderStage == PlaceholderStage.WipeOff }
+ val isWipingOff: Boolean by derivedStateOf { placeholderStage == PlaceholderStage.WipeOff }
/**
* The width of the gradient to use for the placeholder shimmer and wipe-off effects. This is
@@ -567,7 +567,7 @@
internal val gradientXYWidth: Float by derivedStateOf { maxScreenDimension * 2f.pow(1.5f) }
internal var placeholderStage: PlaceholderStage =
- if (isContentReady.value.invoke()) PlaceholderStage.ShowContent
+ if (isContentReady.value.invoke()) PlaceholderStage.HidePlaceholder
else PlaceholderStage.ShowPlaceholder
get() =
derivedStateOf {
@@ -581,12 +581,12 @@
(frameMillis.longValue - startOfWipeOffAnimation) >=
PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS
) {
- field = PlaceholderStage.ShowContent
+ field = PlaceholderStage.HidePlaceholder
}
// Placeholder
} else if (isContentReady.value()) {
if (isReduceMotionEnabled) {
- field = PlaceholderStage.ShowContent
+ field = PlaceholderStage.HidePlaceholder
} else {
startOfWipeOffAnimation = frameMillis.longValue
field = PlaceholderStage.WipeOff
@@ -652,13 +652,14 @@
/**
* Indicates that placeholders no longer to be shown. Enter this stage from [WipeOff] in the
- * loop after the wire-off animation.
+ * loop after the wipe-off animation.
*/
- val ShowContent = PlaceholderStage(2)
+ val HidePlaceholder = PlaceholderStage(2)
/**
* Resets the component to remove the content and reinstate the placeholders so that new
- * content can be loaded. Enter this stage from [ShowContent] and exit to [ShowPlaceholder].
+ * content can be loaded. Enter this stage from [HidePlaceholder] and exit to
+ * [ShowPlaceholder].
*/
val ResetContent = PlaceholderStage(3)
}
@@ -668,7 +669,7 @@
ShowPlaceholder -> "PlaceholderStage.ShowPlaceholder"
WipeOff -> "PlaceholderStage.WipeOff"
ResetContent -> "PlaceholderStage.ResetContent"
- else -> "PlaceholderStage.ShowContent"
+ else -> "PlaceholderStage.HidePlaceholder"
}
}
}
@@ -992,13 +993,23 @@
Brush.linearGradient(
start =
Offset(
- x = placeholderState.placeholderProgression - halfGradientWidth - offset.x,
- y = placeholderState.placeholderProgression - halfGradientWidth - offset.y
+ x =
+ placeholderState.placeholderShimmerProgression -
+ halfGradientWidth -
+ offset.x,
+ y =
+ placeholderState.placeholderShimmerProgression -
+ halfGradientWidth -
+ offset.y
),
end =
Offset(
- x = placeholderState.placeholderProgression + halfGradientWidth - offset.x,
- y = placeholderState.placeholderProgression + halfGradientWidth - offset.y
+ x =
+ placeholderState.placeholderShimmerProgression + halfGradientWidth -
+ offset.x,
+ y =
+ placeholderState.placeholderShimmerProgression + halfGradientWidth -
+ offset.y
),
colorStops =
listOf(
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RadioButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RadioButton.kt
index b5f891f..e846a05 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RadioButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RadioButton.kt
@@ -17,7 +17,7 @@
package androidx.wear.compose.material3
import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.tween
+import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.Interaction
@@ -57,7 +57,6 @@
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
-import androidx.wear.compose.material3.tokens.MotionTokens
import androidx.wear.compose.material3.tokens.RadioButtonTokens
import androidx.wear.compose.material3.tokens.ShapeTokens
import androidx.wear.compose.material3.tokens.SplitRadioButtonTokens
@@ -1418,12 +1417,8 @@
dotColor = color,
onClick = null,
interactionSource = null,
- dotRadiusProgressDuration = { isSelected ->
- if (isSelected) MotionTokens.DurationMedium1 else MotionTokens.DurationShort3
- },
- dotAlphaProgressDuration = MotionTokens.DurationShort3,
- dotAlphaProgressDelay = MotionTokens.DurationShort2,
- easing = MotionTokens.EasingStandardDecelerate,
+ dotRadiusAnimationSpec = PROGRESS_ANIMATION_SPEC,
+ dotAlphaAnimationSpec = PROGRESS_ANIMATION_SPEC,
width = CONTROL_WIDTH,
height = CONTROL_HEIGHT,
ripple = ripple()
@@ -1443,8 +1438,10 @@
}
}
-private val COLOR_ANIMATION_SPEC: AnimationSpec<Color> =
- tween(MotionTokens.DurationMedium1, 0, MotionTokens.EasingStandardDecelerate)
+private val COLOR_ANIMATION_SPEC: AnimationSpec<Color>
+ @Composable get() = MaterialTheme.motionScheme.slowEffectsSpec()
+private val PROGRESS_ANIMATION_SPEC: FiniteAnimationSpec<Float>
+ @Composable get() = MaterialTheme.motionScheme.fastEffectsSpec()
private val SELECTION_CONTROL_WIDTH = 32.dp
private val SELECTION_CONTROL_HEIGHT = 24.dp
private val SELECTION_CONTROL_SPACING = 6.dp
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt
index b2be46e..7a4cd99 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt
@@ -35,7 +35,6 @@
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.withFrameMillis
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -211,15 +210,4 @@
}
}
-private suspend fun waitUntil(condition: () -> Boolean) {
- val initialTimeMillis = withFrameMillis { it }
- while (!condition()) {
- val timeMillis = withFrameMillis { it }
- if (timeMillis - initialTimeMillis > MAX_WAIT_TIME_MILLIS) return
- }
- return
-}
-
-private const val MAX_WAIT_TIME_MILLIS = 1_000L
-
private const val MIN_REQUIRED_ANIMATION_PROGRESS = 0.75f
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SwitchButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SwitchButton.kt
index cd792cf..dc91159 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SwitchButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SwitchButton.kt
@@ -17,9 +17,8 @@
package androidx.wear.compose.material3
import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.TweenSpec
+import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.animateFloat
-import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@@ -66,7 +65,6 @@
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
-import androidx.wear.compose.material3.tokens.MotionTokens
import androidx.wear.compose.material3.tokens.ShapeTokens
import androidx.wear.compose.material3.tokens.SplitSwitchButtonTokens
import androidx.wear.compose.material3.tokens.SwitchButtonTokens
@@ -1940,7 +1938,7 @@
private val SPLIT_MIN_WIDTH = 48.dp
private val SPLIT_SECTIONS_SHAPE = ShapeTokens.CornerExtraSmall
-private val COLOR_ANIMATION_SPEC: AnimationSpec<Color> =
- tween(MotionTokens.DurationMedium1, 0, MotionTokens.EasingStandardDecelerate)
-private val SWITCH_PROGRESS_ANIMATION_SPEC: TweenSpec<Float> =
- tween(MotionTokens.DurationMedium2, 0, MotionTokens.EasingStandardDecelerate)
+private val COLOR_ANIMATION_SPEC: AnimationSpec<Color>
+ @Composable get() = MaterialTheme.motionScheme.slowEffectsSpec()
+private val SWITCH_PROGRESS_ANIMATION_SPEC: FiniteAnimationSpec<Float>
+ @Composable get() = MaterialTheme.motionScheme.fastEffectsSpec()
diff --git a/wear/compose/compose-material3/src/main/res/values-af/strings.xml b/wear/compose/compose-material3/src/main/res/values-af/strings.xml
index 3ab44cf..4d7a998 100644
--- a/wear/compose/compose-material3/src/main/res/values-af/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-af/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Vermeerder"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Verminder"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Vermeerder"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Het misluk"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Sukses"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Maak op foon oop"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-am/strings.xml b/wear/compose/compose-material3/src/main/res/values-am/strings.xml
index 8897e0b..d3c755b 100644
--- a/wear/compose/compose-material3/src/main/res/values-am/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-am/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"ጨምር"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"ቀንስ"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"ጨምር"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"አልተሳካም"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"ተሳክቷል"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"ስልክ ላይ ክፈት"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-ar/strings.xml b/wear/compose/compose-material3/src/main/res/values-ar/strings.xml
index da562e4..02c4b8d 100644
--- a/wear/compose/compose-material3/src/main/res/values-ar/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ar/strings.xml
@@ -54,6 +54,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"زيادة"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"تقليل"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"زيادة"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"تعذر الإجراء"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"نجحَ الإجراء"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"فتح على الهاتف"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-as/strings.xml b/wear/compose/compose-material3/src/main/res/values-as/strings.xml
index 1a3d0b6..0b775bb 100644
--- a/wear/compose/compose-material3/src/main/res/values-as/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-as/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"বঢ়াওক"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"কমাওক"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"বঢ়াওক"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"বিফল হৈছে"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"সফল"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"ফ’নত খোলক"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-az/strings.xml b/wear/compose/compose-material3/src/main/res/values-az/strings.xml
index 2116d76..a649f33 100644
--- a/wear/compose/compose-material3/src/main/res/values-az/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-az/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Artırın"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Azaldın"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Artırın"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Alınmadı"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Alındı"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Telefonda aç"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-b+sr+Latn/strings.xml b/wear/compose/compose-material3/src/main/res/values-b+sr+Latn/strings.xml
index 83aaef1..cc50b58 100644
--- a/wear/compose/compose-material3/src/main/res/values-b+sr+Latn/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-b+sr+Latn/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Povećaj"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Smanji"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Povećaj"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Nije uspelo"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Uspelo"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Na telefonu"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-be/strings.xml b/wear/compose/compose-material3/src/main/res/values-be/strings.xml
index 3504095..44a1d7a 100644
--- a/wear/compose/compose-material3/src/main/res/values-be/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-be/strings.xml
@@ -48,6 +48,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Павялічыць"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Паменшыць"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Павялічыць"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Памылка"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Выканана"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"На тэлефоне"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-bg/strings.xml b/wear/compose/compose-material3/src/main/res/values-bg/strings.xml
index a0d9411..5741a7c 100644
--- a/wear/compose/compose-material3/src/main/res/values-bg/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-bg/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Увеличаване"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Намаляване"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Увеличаване"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Неуспешно"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Успешно"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Отв. на тел."</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-bn/strings.xml b/wear/compose/compose-material3/src/main/res/values-bn/strings.xml
index af0fb65..9ee49c4 100644
--- a/wear/compose/compose-material3/src/main/res/values-bn/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-bn/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"বাড়ান"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"কমানো"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"বাড়ানো"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"সফল হয়নি"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"সফল হয়েছে"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"ফোনে খুলুন"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-bs/strings.xml b/wear/compose/compose-material3/src/main/res/values-bs/strings.xml
index 1c2416d..3f986f8 100644
--- a/wear/compose/compose-material3/src/main/res/values-bs/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-bs/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Povećavanje"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Smanjenje"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Povećanje"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Neuspješno"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Uspješno"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Otvor. na tel."</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-ca/strings.xml b/wear/compose/compose-material3/src/main/res/values-ca/strings.xml
index 1376675..4f4240a 100644
--- a/wear/compose/compose-material3/src/main/res/values-ca/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ca/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Augmenta"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Disminueix"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Augmenta"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Ha fallat"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Correcte"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Obre al telèfon"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-cs/strings.xml b/wear/compose/compose-material3/src/main/res/values-cs/strings.xml
index a914697..33664db 100644
--- a/wear/compose/compose-material3/src/main/res/values-cs/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-cs/strings.xml
@@ -48,6 +48,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Zvýšit"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Snížit"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Zvýšit"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Nezdařilo se"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Hotovo"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Otevřít v telefonu"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-da/strings.xml b/wear/compose/compose-material3/src/main/res/values-da/strings.xml
index 5737e13..842214a 100644
--- a/wear/compose/compose-material3/src/main/res/values-da/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-da/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Øg"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Sænk"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Øg"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Mislykket"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Gennemført"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Åbn på telefon"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-de/strings.xml b/wear/compose/compose-material3/src/main/res/values-de/strings.xml
index b763f40..875685b 100644
--- a/wear/compose/compose-material3/src/main/res/values-de/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-de/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Erhöhen"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Verringern"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Erhöhen"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Fehlgeschlagen"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Abgeschlossen"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Auf Smartphone öffnen"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-el/strings.xml b/wear/compose/compose-material3/src/main/res/values-el/strings.xml
index e0be921..2fb649b 100644
--- a/wear/compose/compose-material3/src/main/res/values-el/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-el/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Αύξηση"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Μείωση"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Αύξηση"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Αποτυχία"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Επιτυχία"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Στο τηλέφωνο"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-en-rAU/strings.xml b/wear/compose/compose-material3/src/main/res/values-en-rAU/strings.xml
index 272e741..b83a6af3 100644
--- a/wear/compose/compose-material3/src/main/res/values-en-rAU/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-en-rAU/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Increase"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Decrease"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Increase"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Failed"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Success"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Open on phone"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-en-rCA/strings.xml b/wear/compose/compose-material3/src/main/res/values-en-rCA/strings.xml
index a1f8f19..33c33d5 100644
--- a/wear/compose/compose-material3/src/main/res/values-en-rCA/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-en-rCA/strings.xml
@@ -42,6 +42,8 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Increase"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Decrease"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Increase"</string>
+ <string name="wear_m3c_alert_dialog_content_description_confirm_button" msgid="7776845597891182382">"Confirm"</string>
+ <string name="wear_m3c_alert_dialog_content_description_dismiss_button" msgid="3572467833850785688">"Dismiss"</string>
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Failed"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Success"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Open on phone"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-en-rGB/strings.xml b/wear/compose/compose-material3/src/main/res/values-en-rGB/strings.xml
index 272e741..b83a6af3 100644
--- a/wear/compose/compose-material3/src/main/res/values-en-rGB/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-en-rGB/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Increase"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Decrease"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Increase"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Failed"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Success"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Open on phone"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-en-rIN/strings.xml b/wear/compose/compose-material3/src/main/res/values-en-rIN/strings.xml
index 272e741..b83a6af3 100644
--- a/wear/compose/compose-material3/src/main/res/values-en-rIN/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-en-rIN/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Increase"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Decrease"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Increase"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Failed"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Success"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Open on phone"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-es-rUS/strings.xml b/wear/compose/compose-material3/src/main/res/values-es-rUS/strings.xml
index 74e4311..56c5f4f 100644
--- a/wear/compose/compose-material3/src/main/res/values-es-rUS/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-es-rUS/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Aumentar"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Disminuir"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Aumentar"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Error"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Listo"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Abrir en el teléfono"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-es/strings.xml b/wear/compose/compose-material3/src/main/res/values-es/strings.xml
index a3f0fb8..1a99712 100644
--- a/wear/compose/compose-material3/src/main/res/values-es/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-es/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Aumentar"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Reducir"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Aumentar"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Error"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Todo correcto"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Ábrelo en el teléfono"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-et/strings.xml b/wear/compose/compose-material3/src/main/res/values-et/strings.xml
index abb3ac0..8d80314 100644
--- a/wear/compose/compose-material3/src/main/res/values-et/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-et/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Suurenda"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Vähenda"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Suurenda"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Ebaõnnestus"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Õnnestus"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Ava telefonis"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-eu/strings.xml b/wear/compose/compose-material3/src/main/res/values-eu/strings.xml
index a44067d..76fc614 100644
--- a/wear/compose/compose-material3/src/main/res/values-eu/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-eu/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Igo"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Jaitsi"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Igo"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Huts egin du"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Eginda"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Ireki telefonoan"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-fa/strings.xml b/wear/compose/compose-material3/src/main/res/values-fa/strings.xml
index 4ae1008..ecf3999 100644
--- a/wear/compose/compose-material3/src/main/res/values-fa/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-fa/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"افزایش دادن"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"کاهش دادن"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"افزایش دادن"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"انجام نشد"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"انجام شد"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"باز کردن در تلفن"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-fi/strings.xml b/wear/compose/compose-material3/src/main/res/values-fi/strings.xml
index 824c7b5..c704c03 100644
--- a/wear/compose/compose-material3/src/main/res/values-fi/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-fi/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Lisää"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Vähennä"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Lisää"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Epäonnistui"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Onnistui"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Puhelimella"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-fr-rCA/strings.xml b/wear/compose/compose-material3/src/main/res/values-fr-rCA/strings.xml
index 6aae002..f6fa0fe 100644
--- a/wear/compose/compose-material3/src/main/res/values-fr-rCA/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-fr-rCA/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Augmenter"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Diminuer"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Augmenter"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Échec"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Réussite"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Ouv. ds tél."</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-fr/strings.xml b/wear/compose/compose-material3/src/main/res/values-fr/strings.xml
index f8cfafe..6cd5c75 100644
--- a/wear/compose/compose-material3/src/main/res/values-fr/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-fr/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Augmenter"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Diminuer"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Augmenter"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Échec"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Opération réussie"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Ouvrir sur le téléphone"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-gl/strings.xml b/wear/compose/compose-material3/src/main/res/values-gl/strings.xml
index c22ed45..985b2f3 100644
--- a/wear/compose/compose-material3/src/main/res/values-gl/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-gl/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Aumentar"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Reducir"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Aumentar"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Erro"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Todo correcto"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Abrir no tel."</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-gu/strings.xml b/wear/compose/compose-material3/src/main/res/values-gu/strings.xml
index 92e9105..5d9c5073 100644
--- a/wear/compose/compose-material3/src/main/res/values-gu/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-gu/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"વધારો"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"ઘટાડો"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"વધારો"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"નિષ્ફળ થઈ"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"સફળ થઈ"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"ફોન પર ખોલો"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-hi/strings.xml b/wear/compose/compose-material3/src/main/res/values-hi/strings.xml
index cafe258..b50a062 100644
--- a/wear/compose/compose-material3/src/main/res/values-hi/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-hi/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"बढ़ाएं"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"घटाएं"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"बढ़ाएं"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"काम नहीं हुआ"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"काम हो गया"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"फ़ोन पर खोलें"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-hr/strings.xml b/wear/compose/compose-material3/src/main/res/values-hr/strings.xml
index 754b6b6..1d6cdd0 100644
--- a/wear/compose/compose-material3/src/main/res/values-hr/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-hr/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Povećaj"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Smanji"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Povećaj"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Nije uspjelo"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Uspjeh"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Na telefonu"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-hu/strings.xml b/wear/compose/compose-material3/src/main/res/values-hu/strings.xml
index 167ca98..c9cf8e1 100644
--- a/wear/compose/compose-material3/src/main/res/values-hu/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-hu/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Növelés"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Csökkentés"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Növelés"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Sikertelen"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Sikerült"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Nyissa meg mobilon"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-hy/strings.xml b/wear/compose/compose-material3/src/main/res/values-hy/strings.xml
index c2b9640..1edfa943 100644
--- a/wear/compose/compose-material3/src/main/res/values-hy/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-hy/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Ավելացնել"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Իջեցնել"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Բարձրացնել"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Ձախողվել է"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Պատրաստ է"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Բացեք հեռախոսում"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-in/strings.xml b/wear/compose/compose-material3/src/main/res/values-in/strings.xml
index 2edf41c..82f3c6e 100644
--- a/wear/compose/compose-material3/src/main/res/values-in/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-in/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Tingkatkan"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Turunkan"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Tingkatkan"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Gagal"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Berhasil"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Buka di ponsel"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-is/strings.xml b/wear/compose/compose-material3/src/main/res/values-is/strings.xml
index a8654ef..494885e 100644
--- a/wear/compose/compose-material3/src/main/res/values-is/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-is/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Auka"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Lækka"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Hækka"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Mistókst"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Tókst"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Opna í símanum"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-it/strings.xml b/wear/compose/compose-material3/src/main/res/values-it/strings.xml
index 1604a5f..dbb4bdf 100644
--- a/wear/compose/compose-material3/src/main/res/values-it/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-it/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Aumenta"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Riduci"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Aumenta"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Non riuscita"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Riuscita"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Su smartph."</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-iw/strings.xml b/wear/compose/compose-material3/src/main/res/values-iw/strings.xml
index 0a0b1cd..27bb9c0 100644
--- a/wear/compose/compose-material3/src/main/res/values-iw/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-iw/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"הגברה"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"הפחתה"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"העלאה"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"הפעולה נכשלה"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"הפעולה הצליחה"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"פתיחה בטלפון"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-ja/strings.xml b/wear/compose/compose-material3/src/main/res/values-ja/strings.xml
index d7ac4f2c..e0d13b8 100644
--- a/wear/compose/compose-material3/src/main/res/values-ja/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ja/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"上げる"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"減らす"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"増やす"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"失敗"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"成功"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"スマホで開く"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-ka/strings.xml b/wear/compose/compose-material3/src/main/res/values-ka/strings.xml
index deda66ae..87a5202 100644
--- a/wear/compose/compose-material3/src/main/res/values-ka/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ka/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"გაზრდა"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"შემცირება"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"გაზრდა"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"ვერ შესრულდა"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"შესრულდა"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"ტელეფონში გახსნა"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-kk/strings.xml b/wear/compose/compose-material3/src/main/res/values-kk/strings.xml
index e3e7615..8af1496 100644
--- a/wear/compose/compose-material3/src/main/res/values-kk/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-kk/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Көбейту"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Азайту"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Көбейту"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Расталмады."</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Расталды."</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Телефоннан ашыңыз."</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-km/strings.xml b/wear/compose/compose-material3/src/main/res/values-km/strings.xml
index 83ca8d5..4ce6dc5 100644
--- a/wear/compose/compose-material3/src/main/res/values-km/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-km/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"បង្កើន"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"បន្ថយ"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"បង្កើន"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"មិនបានសម្រេច"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"ជោគជ័យ"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"បើកលើទូរសព្ទ"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-kn/strings.xml b/wear/compose/compose-material3/src/main/res/values-kn/strings.xml
index 447dc4d..c0bcb1c 100644
--- a/wear/compose/compose-material3/src/main/res/values-kn/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-kn/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"ಹೆಚ್ಚಿಸಿ"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"ಕಡಿಮೆ ಮಾಡಿ"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"ಹೆಚ್ಚಿಸಿ"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"ವಿಫಲವಾಗಿದೆ"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"ಯಶಸ್ವಿಯಾಗಿದೆ"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"ಫೋನ್ನಲ್ಲಿ ತೆರೆಯಿರಿ"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-ko/strings.xml b/wear/compose/compose-material3/src/main/res/values-ko/strings.xml
index f30113a..3870de0 100644
--- a/wear/compose/compose-material3/src/main/res/values-ko/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ko/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"증가"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"줄이기"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"늘리기"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"실패"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"성공"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"휴대전화에서 열기"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-ky/strings.xml b/wear/compose/compose-material3/src/main/res/values-ky/strings.xml
index 4ffd0d3..9e0d4c0 100644
--- a/wear/compose/compose-material3/src/main/res/values-ky/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ky/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Жогорулатуу"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Төмөндөтүү"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Жогорулатуу"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Ишке ашпады"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Ийгилик"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Телефондо ачуу"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-lo/strings.xml b/wear/compose/compose-material3/src/main/res/values-lo/strings.xml
index 52e8a9b..774ced7 100644
--- a/wear/compose/compose-material3/src/main/res/values-lo/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-lo/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"ເພີ່ມຂຶ້ນ"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"ຫຼຸດລົງ"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"ເພີ່ມຂຶ້ນ"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"ບໍ່ສຳເລັດ"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"ສຳເລັດ"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"ເປີດໃນໂທລະສັບ"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-lt/strings.xml b/wear/compose/compose-material3/src/main/res/values-lt/strings.xml
index 28e19fe..5bd1303 100644
--- a/wear/compose/compose-material3/src/main/res/values-lt/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-lt/strings.xml
@@ -48,6 +48,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Padidinti"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Sumažinti"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Padidinti"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Nepavyko"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Pavyko"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Atidaryti telefone"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-lv/strings.xml b/wear/compose/compose-material3/src/main/res/values-lv/strings.xml
index 94d0107..f355546 100644
--- a/wear/compose/compose-material3/src/main/res/values-lv/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-lv/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Palielināt"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Samazināt"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Palielināt"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Neizdevās"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Izdevās"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Atvērt tālrunī"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-mk/strings.xml b/wear/compose/compose-material3/src/main/res/values-mk/strings.xml
index f2cd97d..3f556e4 100644
--- a/wear/compose/compose-material3/src/main/res/values-mk/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-mk/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Зголеми"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Намалување"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Зголемување"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Неуспешно"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Успешно"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Отвори на телефонот"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-ml/strings.xml b/wear/compose/compose-material3/src/main/res/values-ml/strings.xml
index 4d26863..632c921 100644
--- a/wear/compose/compose-material3/src/main/res/values-ml/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ml/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"കൂട്ടുക"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"കുറയ്ക്കുക"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"കൂട്ടുക"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"പരാജയപ്പെട്ടു"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"വിജയിച്ചു"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"ഫോണിൽ തുറക്കൂ"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-mn/strings.xml b/wear/compose/compose-material3/src/main/res/values-mn/strings.xml
index 3b24ce1..582aa3a 100644
--- a/wear/compose/compose-material3/src/main/res/values-mn/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-mn/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Нэмэгдүүлэх"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Багасгах"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Нэмэгдүүлэх"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Амжилтгүй"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Амжилттай"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Утсанд нээх"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-mr/strings.xml b/wear/compose/compose-material3/src/main/res/values-mr/strings.xml
index e6efee5..acbfd1c 100644
--- a/wear/compose/compose-material3/src/main/res/values-mr/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-mr/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"वाढवा"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"कमी करा"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"वाढवा"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"अयशस्वी"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"यशस्वी झाले"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"फोनवर उघडा"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-ms/strings.xml b/wear/compose/compose-material3/src/main/res/values-ms/strings.xml
index b41938c..9a77a5d 100644
--- a/wear/compose/compose-material3/src/main/res/values-ms/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ms/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Tambahkan"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Kurangkan"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Tambahkan"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Gagal"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Berjaya"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Buka pada telefon"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-my/strings.xml b/wear/compose/compose-material3/src/main/res/values-my/strings.xml
index 678ad76..da6e684 100644
--- a/wear/compose/compose-material3/src/main/res/values-my/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-my/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"တိုးရန်"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"လျှော့ရန်"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"ချဲ့ရန်"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"မအောင်မြင်ပါ"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"အောင်မြင်သည်"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"ဖုန်း၌ဖွင့်ရန်"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-nb/strings.xml b/wear/compose/compose-material3/src/main/res/values-nb/strings.xml
index 664750c..ef7e2f0 100644
--- a/wear/compose/compose-material3/src/main/res/values-nb/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-nb/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Øk"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Reduser"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Øk"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Mislyktes"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Vellykket"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Åpne på tlf."</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-ne/strings.xml b/wear/compose/compose-material3/src/main/res/values-ne/strings.xml
index fe60e64..fd2cf9b 100644
--- a/wear/compose/compose-material3/src/main/res/values-ne/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ne/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"बढाउनुहोस्"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"घटाउनुहोस्"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"बढाउनुहोस्"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"पुष्टि गर्न सकिएन"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"पुष्टि गरियो"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"फोनमा खोल्नुहोस्"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-nl/strings.xml b/wear/compose/compose-material3/src/main/res/values-nl/strings.xml
index a267de1..34beeaa 100644
--- a/wear/compose/compose-material3/src/main/res/values-nl/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-nl/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Verhogen"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Verlagen"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Verhogen"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Mislukt"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Geslaagd"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Openen op telefoon"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-or/strings.xml b/wear/compose/compose-material3/src/main/res/values-or/strings.xml
index d356304..1b9346e 100644
--- a/wear/compose/compose-material3/src/main/res/values-or/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-or/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"ବଢ଼ାନ୍ତୁ"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"କମାନ୍ତୁ"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"ବଢ଼ାନ୍ତୁ"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"ବିଫଳ ହୋଇଛି"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"ସଫଳ"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"ଫୋନରେ ଖୋଲନ୍ତୁ"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-pa/strings.xml b/wear/compose/compose-material3/src/main/res/values-pa/strings.xml
index 3c68908..96306b7 100644
--- a/wear/compose/compose-material3/src/main/res/values-pa/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-pa/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"ਵਧਾਓ"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"ਘਟਾਓ"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"ਵਧਾਓ"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"ਅਸਫਲ ਰਿਹਾ"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"ਸਫਲ ਰਿਹਾ"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"ਫ਼ੋਨ \'ਤੇ ਖੋਲ੍ਹੋ"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-pl/strings.xml b/wear/compose/compose-material3/src/main/res/values-pl/strings.xml
index a9249328..fecfbf7 100644
--- a/wear/compose/compose-material3/src/main/res/values-pl/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-pl/strings.xml
@@ -48,6 +48,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Zwiększ"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Zmniejsz"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Zwiększ"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Niepowodzenie"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Udało się"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Otwórz na telefonie"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-pt-rBR/strings.xml b/wear/compose/compose-material3/src/main/res/values-pt-rBR/strings.xml
index 775ebcb..e5ab9320 100644
--- a/wear/compose/compose-material3/src/main/res/values-pt-rBR/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-pt-rBR/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Aumentar"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Diminuir"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Aumentar"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Falha"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Pronto"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Abra no smartphone"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-pt-rPT/strings.xml b/wear/compose/compose-material3/src/main/res/values-pt-rPT/strings.xml
index a99c8c1..15c07d1 100644
--- a/wear/compose/compose-material3/src/main/res/values-pt-rPT/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-pt-rPT/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Aumentar"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Diminuir"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Aumentar"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Falhou"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Concluído"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Abrir no tel."</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-pt/strings.xml b/wear/compose/compose-material3/src/main/res/values-pt/strings.xml
index 775ebcb..e5ab9320 100644
--- a/wear/compose/compose-material3/src/main/res/values-pt/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-pt/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Aumentar"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Diminuir"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Aumentar"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Falha"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Pronto"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Abra no smartphone"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-ro/strings.xml b/wear/compose/compose-material3/src/main/res/values-ro/strings.xml
index b2920e2..8e21c1b 100644
--- a/wear/compose/compose-material3/src/main/res/values-ro/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ro/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Crește"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Scade"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Crește"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Eroare"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Succes"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Pe telefon"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-ru/strings.xml b/wear/compose/compose-material3/src/main/res/values-ru/strings.xml
index 38b6613..bfa21739 100644
--- a/wear/compose/compose-material3/src/main/res/values-ru/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ru/strings.xml
@@ -48,6 +48,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Увеличить"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Уменьшить"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Увеличить"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Ошибка"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Готово"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Открыть на телефоне"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-si/strings.xml b/wear/compose/compose-material3/src/main/res/values-si/strings.xml
index db2458b..37aa465 100644
--- a/wear/compose/compose-material3/src/main/res/values-si/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-si/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"වැඩි කරන්න"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"අඩු කරන්න"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"වැඩි කරන්න"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"අසමත් විය"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"සාර්ථකයි"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"දුරකථනයෙන් විවෘත කරන්න"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-sk/strings.xml b/wear/compose/compose-material3/src/main/res/values-sk/strings.xml
index 1569d57..30e57bb 100644
--- a/wear/compose/compose-material3/src/main/res/values-sk/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sk/strings.xml
@@ -48,6 +48,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Zvýšiť"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Znížiť"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Zvýšiť"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Neúspešné"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Podarilo sa"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Otvorte v telefóne"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-sl/strings.xml b/wear/compose/compose-material3/src/main/res/values-sl/strings.xml
index cb50296..54c0542 100644
--- a/wear/compose/compose-material3/src/main/res/values-sl/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sl/strings.xml
@@ -48,6 +48,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Povečanje"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Zmanjšaj"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Povečaj"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Neuspešno"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Uspešno"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Odpri v telefonu"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-sq/strings.xml b/wear/compose/compose-material3/src/main/res/values-sq/strings.xml
index 0ecac90..ba1133a 100644
--- a/wear/compose/compose-material3/src/main/res/values-sq/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sq/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Rrit"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Ul"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Rrit"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Dështoi"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Me sukses"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Hape në telefon"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-sr/strings.xml b/wear/compose/compose-material3/src/main/res/values-sr/strings.xml
index 78c8d8c..44ef98d 100644
--- a/wear/compose/compose-material3/src/main/res/values-sr/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sr/strings.xml
@@ -45,6 +45,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Повећај"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Смањи"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Повећај"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Није успело"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Успело"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"На телефону"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-sv/strings.xml b/wear/compose/compose-material3/src/main/res/values-sv/strings.xml
index c8d50d9..e388d545 100644
--- a/wear/compose/compose-material3/src/main/res/values-sv/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sv/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Öka"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Minska"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Öka"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Misslyckades"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Klart"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"På telefonen"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-sw/strings.xml b/wear/compose/compose-material3/src/main/res/values-sw/strings.xml
index 78cb7e0..bbdd5f8 100644
--- a/wear/compose/compose-material3/src/main/res/values-sw/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sw/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Ongeza"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Punguza"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Ongeza"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Imeshindwa"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Imemaliza"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Fungua kwenye simu"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-ta/strings.xml b/wear/compose/compose-material3/src/main/res/values-ta/strings.xml
index b8952f2..18b4454 100644
--- a/wear/compose/compose-material3/src/main/res/values-ta/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ta/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"அதிகரிக்கும்"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"குறைக்கும்"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"அதிகரிக்கும்"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"தோல்வி"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"முடிந்தது"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"மொபைலில் திற"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-te/strings.xml b/wear/compose/compose-material3/src/main/res/values-te/strings.xml
index 3664364..b0ad3e6 100644
--- a/wear/compose/compose-material3/src/main/res/values-te/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-te/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"పెంచండి"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"తగ్గించండి"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"పెంచండి"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"విఫలమైంది"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"విజయవంతమైంది"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"ఫోన్లో తెరు"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-th/strings.xml b/wear/compose/compose-material3/src/main/res/values-th/strings.xml
index f82c0a5..fc10a30 100644
--- a/wear/compose/compose-material3/src/main/res/values-th/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-th/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"เพิ่ม"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"ลด"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"เพิ่ม"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"ไม่สำเร็จ"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"สำเร็จ"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"เปิดในโทรศัพท์"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-tl/strings.xml b/wear/compose/compose-material3/src/main/res/values-tl/strings.xml
index c9781cd..c51056a 100644
--- a/wear/compose/compose-material3/src/main/res/values-tl/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-tl/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Dagdagan"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Bawasan"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Dagdagan"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Nabigo"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Matagumpay"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Buksan"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-tr/strings.xml b/wear/compose/compose-material3/src/main/res/values-tr/strings.xml
index e950de0..425a8af 100644
--- a/wear/compose/compose-material3/src/main/res/values-tr/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-tr/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Artır"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Azalt"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Artır"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Başarısız"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Başarılı"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Telefonda aç"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-uk/strings.xml b/wear/compose/compose-material3/src/main/res/values-uk/strings.xml
index 9c8f77f..b09239b 100644
--- a/wear/compose/compose-material3/src/main/res/values-uk/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-uk/strings.xml
@@ -48,6 +48,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Збільшити"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Зменшити"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Збільшити"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Помилка"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Готово"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"На телефоні"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-ur/strings.xml b/wear/compose/compose-material3/src/main/res/values-ur/strings.xml
index 4c82b62..a6c5a45 100644
--- a/wear/compose/compose-material3/src/main/res/values-ur/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ur/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"بڑھائیں"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"کم کریں"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"بڑھائیں"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"ناکام ہوا"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"کامیاب"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"فون پر کھولیں"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-uz/strings.xml b/wear/compose/compose-material3/src/main/res/values-uz/strings.xml
index 3a0749d..038a7ef 100644
--- a/wear/compose/compose-material3/src/main/res/values-uz/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-uz/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Oshirish"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Pasaytirish"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Oshirish"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Bajarilmadi"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Bajarildi"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Telefonda"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-vi/strings.xml b/wear/compose/compose-material3/src/main/res/values-vi/strings.xml
index e1a9df8..5110168 100644
--- a/wear/compose/compose-material3/src/main/res/values-vi/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-vi/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Tăng"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Giảm"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Tăng"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Lỗi"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Thành công"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Mở trên điện thoại"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-zh-rCN/strings.xml b/wear/compose/compose-material3/src/main/res/values-zh-rCN/strings.xml
index 7f82188..bcbe3a2 100644
--- a/wear/compose/compose-material3/src/main/res/values-zh-rCN/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-zh-rCN/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"增加"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"降低"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"提高"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"失败"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"成功"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"在手机上打开"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-zh-rHK/strings.xml b/wear/compose/compose-material3/src/main/res/values-zh-rHK/strings.xml
index e87a261..7cedc50 100644
--- a/wear/compose/compose-material3/src/main/res/values-zh-rHK/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-zh-rHK/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"調高"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"調低"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"調高"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"失敗"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"成功"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"在手機開啟"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-zh-rTW/strings.xml b/wear/compose/compose-material3/src/main/res/values-zh-rTW/strings.xml
index 311e77d9..c330c20 100644
--- a/wear/compose/compose-material3/src/main/res/values-zh-rTW/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-zh-rTW/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"調高"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"調低"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"調高"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"失敗"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"成功"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"在手機上開啟"</string>
diff --git a/wear/compose/compose-material3/src/main/res/values-zu/strings.xml b/wear/compose/compose-material3/src/main/res/values-zu/strings.xml
index b71059a..d9b5dbc 100644
--- a/wear/compose/compose-material3/src/main/res/values-zu/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-zu/strings.xml
@@ -42,6 +42,10 @@
<string name="wear_m3c_slider_increase_content_description" msgid="3329631766954416834">"Khulisa"</string>
<string name="wear_m3c_stepper_decrease_content_description" msgid="6939134411425530620">"Yehlisa"</string>
<string name="wear_m3c_stepper_increase_content_description" msgid="6513575827514139918">"Khulisa"</string>
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_confirm_button (7776845597891182382) -->
+ <skip />
+ <!-- no translation found for wear_m3c_alert_dialog_content_description_dismiss_button (3572467833850785688) -->
+ <skip />
<string name="wear_m3c_confirmation_failure_message" msgid="6690105830380889949">"Yehlulekile"</string>
<string name="wear_m3c_confirmation_success_message" msgid="7045594078216655038">"Impumelelo"</string>
<string name="wear_m3c_open_on_phone" msgid="5829463187924353601">"Vula efonini"</string>
diff --git a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/ButtonBenchmarkBase.kt b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/ButtonBenchmarkBase.kt
index 9ba55cc..3fdfc12 100644
--- a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/ButtonBenchmarkBase.kt
+++ b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/ButtonBenchmarkBase.kt
@@ -18,7 +18,9 @@
import android.content.Intent
import androidx.benchmark.macro.CompilationMode
-import androidx.benchmark.macro.FrameTimingMetric
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.FrameTimingGfxInfoMetric
+import androidx.benchmark.macro.MemoryUsageMetric
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.uiautomator.By
import org.junit.After
@@ -26,6 +28,7 @@
import org.junit.Rule
import org.junit.Test
+@OptIn(ExperimentalMetricApi::class)
abstract class ButtonBenchmarkBase(
private val compilationMode: CompilationMode,
private val activityAction: String
@@ -46,7 +49,8 @@
fun start() {
benchmarkRule.measureRepeated(
packageName = PACKAGE_NAME,
- metrics = listOf(FrameTimingMetric()),
+ metrics =
+ listOf(FrameTimingGfxInfoMetric(), MemoryUsageMetric(MemoryUsageMetric.Mode.Last)),
compilationMode = compilationMode,
iterations = 10,
setupBlock = {
diff --git a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/PositionIndicatorBenchmark.kt b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/PositionIndicatorBenchmark.kt
index 22b55a3..9662bfc 100644
--- a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/PositionIndicatorBenchmark.kt
+++ b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/PositionIndicatorBenchmark.kt
@@ -18,7 +18,9 @@
import android.content.Intent
import androidx.benchmark.macro.CompilationMode
-import androidx.benchmark.macro.FrameTimingMetric
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.FrameTimingGfxInfoMetric
+import androidx.benchmark.macro.MemoryUsageMetric
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.filters.LargeTest
import androidx.test.uiautomator.By
@@ -32,6 +34,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
+@OptIn(ExperimentalMetricApi::class)
@LargeTest
@RunWith(Parameterized::class)
class PositionIndicatorBenchmark(private val compilationMode: CompilationMode) {
@@ -51,7 +54,8 @@
fun start() {
benchmarkRule.measureRepeated(
packageName = PACKAGE_NAME,
- metrics = listOf(FrameTimingMetric()),
+ metrics =
+ listOf(FrameTimingGfxInfoMetric(), MemoryUsageMetric(MemoryUsageMetric.Mode.Last)),
compilationMode = compilationMode,
iterations = 5,
setupBlock = {
diff --git a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/ScrollBenchmark.kt b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/ScrollBenchmark.kt
index b3704cc..f357f35 100644
--- a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/ScrollBenchmark.kt
+++ b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/ScrollBenchmark.kt
@@ -19,7 +19,9 @@
import android.content.Intent
import android.graphics.Point
import androidx.benchmark.macro.CompilationMode
-import androidx.benchmark.macro.FrameTimingMetric
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.FrameTimingGfxInfoMetric
+import androidx.benchmark.macro.MemoryUsageMetric
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.filters.LargeTest
import androidx.test.uiautomator.By
@@ -31,6 +33,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
+@OptIn(ExperimentalMetricApi::class)
@LargeTest
@RunWith(Parameterized::class)
class ScrollBenchmark(private val compilationMode: CompilationMode) {
@@ -50,7 +53,11 @@
fun start() {
benchmarkRule.measureRepeated(
packageName = PACKAGE_NAME,
- metrics = listOf(FrameTimingMetric()),
+ metrics =
+ listOf(
+ FrameTimingGfxInfoMetric(),
+ MemoryUsageMetric(MemoryUsageMetric.Mode.Last),
+ ),
compilationMode = compilationMode,
iterations = 10,
setupBlock = {
diff --git a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/SwipeToDismissBenchmark.kt b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/SwipeToDismissBenchmark.kt
index fc09578..4e4a9072 100644
--- a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/SwipeToDismissBenchmark.kt
+++ b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/SwipeToDismissBenchmark.kt
@@ -18,7 +18,9 @@
import android.content.Intent
import androidx.benchmark.macro.CompilationMode
-import androidx.benchmark.macro.FrameTimingMetric
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.FrameTimingGfxInfoMetric
+import androidx.benchmark.macro.MemoryUsageMetric
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.filters.LargeTest
import androidx.test.uiautomator.By
@@ -31,6 +33,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
+@OptIn(ExperimentalMetricApi::class)
@LargeTest
@RunWith(Parameterized::class)
class SwipeToDismissBenchmark(private val compilationMode: CompilationMode) {
@@ -50,7 +53,11 @@
fun start() {
benchmarkRule.measureRepeated(
packageName = PACKAGE_NAME,
- metrics = listOf(FrameTimingMetric()),
+ metrics =
+ listOf(
+ FrameTimingGfxInfoMetric(),
+ MemoryUsageMetric(MemoryUsageMetric.Mode.Last),
+ ),
compilationMode = compilationMode,
iterations = 10,
setupBlock = {
diff --git a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/SwipeToRevealBenchmark.kt b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/SwipeToRevealBenchmark.kt
index 9845b66..9786346 100644
--- a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/SwipeToRevealBenchmark.kt
+++ b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/SwipeToRevealBenchmark.kt
@@ -18,7 +18,9 @@
import android.content.Intent
import androidx.benchmark.macro.CompilationMode
-import androidx.benchmark.macro.FrameTimingMetric
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.FrameTimingGfxInfoMetric
+import androidx.benchmark.macro.MemoryUsageMetric
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.filters.LargeTest
import androidx.test.uiautomator.By
@@ -31,6 +33,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
+@OptIn(ExperimentalMetricApi::class)
@LargeTest
@RunWith(Parameterized::class)
class SwipeToRevealBenchmark(private val compilationMode: CompilationMode) {
@@ -50,7 +53,11 @@
fun start() {
benchmarkRule.measureRepeated(
packageName = PACKAGE_NAME,
- metrics = listOf(FrameTimingMetric()),
+ metrics =
+ listOf(
+ FrameTimingGfxInfoMetric(),
+ MemoryUsageMetric(MemoryUsageMetric.Mode.Last),
+ ),
compilationMode = compilationMode,
iterations = 10,
setupBlock = {
diff --git a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/material3/DialogBenchmark.kt b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/material3/DialogBenchmark.kt
index 337bb62..fa6aa78 100644
--- a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/material3/DialogBenchmark.kt
+++ b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/material3/DialogBenchmark.kt
@@ -18,7 +18,9 @@
import android.content.Intent
import androidx.benchmark.macro.CompilationMode
-import androidx.benchmark.macro.FrameTimingMetric
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.FrameTimingGfxInfoMetric
+import androidx.benchmark.macro.MemoryUsageMetric
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.filters.LargeTest
import androidx.test.uiautomator.By
@@ -33,6 +35,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
+@OptIn(ExperimentalMetricApi::class)
@LargeTest
@RunWith(Parameterized::class)
class DialogBenchmark(private val compilationMode: CompilationMode) {
@@ -52,7 +55,8 @@
fun start() {
benchmarkRule.measureRepeated(
packageName = PACKAGE_NAME,
- metrics = listOf(FrameTimingMetric()),
+ metrics =
+ listOf(FrameTimingGfxInfoMetric(), MemoryUsageMetric(MemoryUsageMetric.Mode.Last)),
compilationMode = compilationMode,
iterations = 5,
setupBlock = {
diff --git a/wear/protolayout/protolayout-proto/src/main/proto/layout.proto b/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
index 540382b..b235c5a 100644
--- a/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
+++ b/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
@@ -736,6 +736,45 @@
Shadow shadow = 2;
}
+// A dashed arc line that can be placed in an Arc container. It is an arc line made up of arc line
+// segments separated by gaps.
+message DashedArcLine {
+ // The length of this line in degrees, including gaps. <setter> If not defined,
+ // defaults to 0.</setter>
+ //
+ // When using a dynamic value, make sure to specify the bounding constraints
+ // for the affected layout element through {@code
+ // setLayoutConstraintsForDynamicLength(AngularLayoutConstraint)} otherwise
+ // {@code build()} fails.
+ DegreesProp length = 1;
+
+ // The thickness of this line. <setter> If not defined, defaults to 0.</setter>
+ DpProp thickness = 2;
+
+ // The color of this line.
+ ColorProp color = 3;
+
+ // Modifiers for this element.
+ ArcModifiers modifiers = 4;
+
+ // The direction in which this line is drawn.<setter> If not set, defaults to
+ // ARC_DIRECTION_CLOCKWISE.</setter>
+ ArcDirectionProp arc_direction = 5;
+
+ // The dashed line pattern which describes how the arc line is segmented by gaps.
+ DashedLinePattern line_pattern = 6;
+}
+
+// A dashed line pattern which describes how the dashed arc line is segmented by gaps.
+message DashedLinePattern {
+ // The size in dp of the gap between the line segments.<setter> If not
+ // defined, defaults to 0.</setter>
+ DpProp gap_size = 1;
+
+ // The list of each gap's center location in degrees.
+ repeated DegreesProp gap_locations = 2;
+}
+
// A simple spacer used to provide padding between adjacent elements in an Arc.
message ArcSpacer {
// The length of this spacer, in degrees. If not defined, defaults to 0.
@@ -837,6 +876,7 @@
ArcLine line = 2;
ArcSpacer spacer = 3;
ArcAdapter adapter = 4;
+ DashedArcLine dashed_line = 5;
}
}
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
index 3be0537..d8ed179 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
@@ -3961,6 +3961,10 @@
parentViewWrapper, element.getText(), nodePosId, pipelineMaker);
break;
+ case DASHED_LINE:
+ // TODO: b/360314390 - inflate a dashed arc here with WearDashedArcLineView
+ break;
+
case INNER_NOT_SET:
break;
}
diff --git a/wear/protolayout/protolayout-testing/api/current.txt b/wear/protolayout/protolayout-testing/api/current.txt
index e6f50d0..dd57766 100644
--- a/wear/protolayout/protolayout-testing/api/current.txt
+++ b/wear/protolayout/protolayout-testing/api/current.txt
@@ -1 +1,25 @@
// Signature format: 4.0
+package androidx.wear.protolayout.testing {
+
+ public final class LayoutElementAssertion {
+ method public androidx.wear.protolayout.testing.LayoutElementAssertion assert(androidx.wear.protolayout.testing.LayoutElementMatcher matcher);
+ method public void assertDoesNotExist();
+ method public void assertExists();
+ }
+
+ public final class LayoutElementAssertionsProvider {
+ ctor public LayoutElementAssertionsProvider(androidx.wear.protolayout.LayoutElementBuilders.Layout layout);
+ ctor public LayoutElementAssertionsProvider(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement layoutRoot);
+ method public androidx.wear.protolayout.testing.LayoutElementAssertion onElement(androidx.wear.protolayout.testing.LayoutElementMatcher matcher);
+ method public androidx.wear.protolayout.testing.LayoutElementAssertion onRoot();
+ }
+
+ public final class LayoutElementMatcher {
+ ctor public LayoutElementMatcher(String description, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.LayoutElementBuilders.LayoutElement,java.lang.Boolean> matcher);
+ method public infix androidx.wear.protolayout.testing.LayoutElementMatcher and(androidx.wear.protolayout.testing.LayoutElementMatcher other);
+ method public operator androidx.wear.protolayout.testing.LayoutElementMatcher not();
+ method public infix androidx.wear.protolayout.testing.LayoutElementMatcher or(androidx.wear.protolayout.testing.LayoutElementMatcher other);
+ }
+
+}
+
diff --git a/wear/protolayout/protolayout-testing/api/restricted_current.txt b/wear/protolayout/protolayout-testing/api/restricted_current.txt
index e6f50d0..dd57766 100644
--- a/wear/protolayout/protolayout-testing/api/restricted_current.txt
+++ b/wear/protolayout/protolayout-testing/api/restricted_current.txt
@@ -1 +1,25 @@
// Signature format: 4.0
+package androidx.wear.protolayout.testing {
+
+ public final class LayoutElementAssertion {
+ method public androidx.wear.protolayout.testing.LayoutElementAssertion assert(androidx.wear.protolayout.testing.LayoutElementMatcher matcher);
+ method public void assertDoesNotExist();
+ method public void assertExists();
+ }
+
+ public final class LayoutElementAssertionsProvider {
+ ctor public LayoutElementAssertionsProvider(androidx.wear.protolayout.LayoutElementBuilders.Layout layout);
+ ctor public LayoutElementAssertionsProvider(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement layoutRoot);
+ method public androidx.wear.protolayout.testing.LayoutElementAssertion onElement(androidx.wear.protolayout.testing.LayoutElementMatcher matcher);
+ method public androidx.wear.protolayout.testing.LayoutElementAssertion onRoot();
+ }
+
+ public final class LayoutElementMatcher {
+ ctor public LayoutElementMatcher(String description, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.LayoutElementBuilders.LayoutElement,java.lang.Boolean> matcher);
+ method public infix androidx.wear.protolayout.testing.LayoutElementMatcher and(androidx.wear.protolayout.testing.LayoutElementMatcher other);
+ method public operator androidx.wear.protolayout.testing.LayoutElementMatcher not();
+ method public infix androidx.wear.protolayout.testing.LayoutElementMatcher or(androidx.wear.protolayout.testing.LayoutElementMatcher other);
+ }
+
+}
+
diff --git a/wear/protolayout/protolayout-testing/build.gradle b/wear/protolayout/protolayout-testing/build.gradle
index 565226d..443c60b 100644
--- a/wear/protolayout/protolayout-testing/build.gradle
+++ b/wear/protolayout/protolayout-testing/build.gradle
@@ -31,16 +31,32 @@
dependencies {
api(libs.kotlinStdlib)
+ api(project(":wear:protolayout:protolayout"))
+ api(project(":wear:protolayout:protolayout-expression"))
implementation("androidx.annotation:annotation:1.8.1")
+ implementation(project(":wear:protolayout:protolayout-proto"))
+ implementation(project(":wear:protolayout:protolayout-expression-pipeline"))
+
+ testImplementation(libs.junit)
+ testImplementation(libs.robolectric)
+ testImplementation(libs.testExtJunit)
+ testImplementation(libs.truth)
}
android {
+ defaultConfig {
+ minSdkVersion 26
+ }
namespace "androidx.wear.protolayout.testing"
+
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
}
androidx {
name = "androidx.wear.protolayout:protolayout-testing"
- type = LibraryType.PUBLISHED_LIBRARY
+ type = LibraryType.PUBLISHED_TEST_LIBRARY
inceptionYear = "2024"
description = "Testing framework for protolayout element tree."
}
diff --git a/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/LayoutElementAssertion.kt b/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/LayoutElementAssertion.kt
new file mode 100644
index 0000000..f34070d
--- /dev/null
+++ b/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/LayoutElementAssertion.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2024 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.wear.protolayout.testing
+
+import androidx.wear.protolayout.LayoutElementBuilders
+import java.lang.AssertionError
+
+/**
+ * Represents a layout element that can be asserted on.
+ *
+ * <p>An instance of [LayoutElementAssertion] can be obtained from 'onElement' method on a
+ * [LayoutElementAssertionsProvider].
+ */
+public class LayoutElementAssertion
+internal constructor(
+ private val elementDescription: String,
+ internal val element: LayoutElementBuilders.LayoutElement?,
+) {
+ /** Asserts that the element was found in the element tree. */
+ public fun assertExists() {
+ if (element == null) {
+ throw AssertionError("Expected $elementDescription to exist, but it does not.")
+ }
+ }
+
+ /** Asserts that no element was found in the element tree. */
+ public fun assertDoesNotExist() {
+ if (element != null) {
+ throw AssertionError("Expected $elementDescription to not exist, but it does.")
+ }
+ }
+
+ /** Asserts that the provided [LayoutElementMatcher] is satisfied for this element. */
+ public fun assert(matcher: LayoutElementMatcher): LayoutElementAssertion {
+ assertExists()
+ if (!matcher.matches(element!!)) {
+ throw AssertionError(
+ "Expected $elementDescription to match '${matcher.description}'," +
+ " but it does not."
+ )
+ }
+ return this
+ }
+}
diff --git a/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/LayoutElementAssertionsProvider.kt b/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/LayoutElementAssertionsProvider.kt
new file mode 100644
index 0000000..4971419
--- /dev/null
+++ b/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/LayoutElementAssertionsProvider.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2024 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.wear.protolayout.testing
+
+import androidx.wear.protolayout.LayoutElementBuilders.Layout
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement
+import androidx.wear.protolayout.LayoutElementBuilders.layoutElementFromProto
+
+/** Provides the main entry point into testing by exposing methods to find a layout element. */
+public class LayoutElementAssertionsProvider(layoutRoot: LayoutElement) {
+ private val root: LayoutElement =
+ layoutElementFromProto(layoutRoot.toLayoutElementProto(), null)
+
+ public constructor(layout: Layout) : this(layout.root!!)
+
+ /** Finds an element that matches the given condition. */
+ public fun onElement(matcher: LayoutElementMatcher): LayoutElementAssertion {
+ val elementDescription = "element matching '${matcher.description}'"
+ return LayoutElementAssertion(elementDescription, searchElement(root, matcher))
+ }
+
+ /**
+ * Finds the top level element of the element tree added to this
+ * [LayoutElementAssertionsProvider].
+ */
+ public fun onRoot(): LayoutElementAssertion = LayoutElementAssertion("root", root)
+
+ // TODO - b/374944199: add onAllElement which returns a LayoutElementAssertionCollection
+}
diff --git a/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/LayoutElementMatcher.kt b/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/LayoutElementMatcher.kt
new file mode 100644
index 0000000..77aa0c9
--- /dev/null
+++ b/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/LayoutElementMatcher.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2024 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.wear.protolayout.testing
+
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement
+
+/**
+ * Wrapper for element matcher lambdas that allows to build string explaining to the developer what
+ * conditions are being tested.
+ *
+ * @param description a string explaining to the developer what conditions were being tested.
+ * @param matcher a lambda performing the actual logic of matching on the layout element.
+ */
+public class LayoutElementMatcher(
+ internal val description: String,
+ private val matcher: (LayoutElement) -> Boolean
+) {
+ /** Returns whether the given element is matched by this matcher. */
+ internal fun matches(element: LayoutElement): Boolean = matcher(element)
+
+ /**
+ * Returns whether the given element is matched by both this and the other mather.
+ *
+ * @param other mather that should also match in addition to current matcher.
+ */
+ public infix fun and(other: LayoutElementMatcher): LayoutElementMatcher =
+ LayoutElementMatcher("($description) && (${other.description})") {
+ matcher(it) && other.matches(it)
+ }
+
+ /**
+ * Returns whether the given element is matched by this or the other mather.
+ *
+ * @param other mather that can be tested to match if the current matcher does not.
+ */
+ public infix fun or(other: LayoutElementMatcher): LayoutElementMatcher =
+ LayoutElementMatcher("($description) || (${other.description})") {
+ matcher(it) || other.matches(it)
+ }
+
+ /** Returns whether the given element does not match the matcher. */
+ public operator fun not(): LayoutElementMatcher =
+ LayoutElementMatcher("NOT ($description)") { !matcher(it) }
+}
diff --git a/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/helpers.kt b/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/helpers.kt
new file mode 100644
index 0000000..de300bc
--- /dev/null
+++ b/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/helpers.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2024 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.wear.protolayout.testing
+
+import androidx.wear.protolayout.LayoutElementBuilders.Box
+import androidx.wear.protolayout.LayoutElementBuilders.Column
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement
+import androidx.wear.protolayout.LayoutElementBuilders.Row
+
+internal val LayoutElement.children: List<LayoutElement>
+ get() =
+ when (this) {
+ is Box -> this.contents
+ is Row -> this.contents
+ is Column -> this.contents
+ // TODO b/372916396 - Dealing with Arc container and ArcLayoutElements
+ else -> emptyList<LayoutElement>()
+ }
+
+internal fun searchElement(root: LayoutElement?, matcher: LayoutElementMatcher): LayoutElement? {
+ if (root == null) return null
+ if (matcher.matches(root)) return root
+ return root.children.firstNotNullOfOrNull { searchElement(it, matcher) }
+}
diff --git a/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/package-info.java b/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/package-info.java
index cef88a5..46afd06 100644
--- a/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/package-info.java
+++ b/wear/protolayout/protolayout-testing/src/main/java/androidx/wear/protolayout/testing/package-info.java
@@ -14,8 +14,4 @@
* limitations under the License.
*/
-// TODO(b/371013214): Make protolayout-testing library public.
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
package androidx.wear.protolayout.testing;
-
-import androidx.annotation.RestrictTo;
diff --git a/wear/protolayout/protolayout-testing/src/test/java/androidx/wear/protolayout/testing/LayoutElementAssertionTest.kt b/wear/protolayout/protolayout-testing/src/test/java/androidx/wear/protolayout/testing/LayoutElementAssertionTest.kt
new file mode 100644
index 0000000..924796a
--- /dev/null
+++ b/wear/protolayout/protolayout-testing/src/test/java/androidx/wear/protolayout/testing/LayoutElementAssertionTest.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2024 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.wear.protolayout.testing
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.wear.protolayout.LayoutElementBuilders.Box
+import androidx.wear.protolayout.LayoutElementBuilders.Text
+import com.google.common.truth.ExpectFailure.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.internal.DoNotInstrument
+
+@RunWith(AndroidJUnit4::class)
+@DoNotInstrument
+class LayoutElementAssertionTest {
+
+ @Test
+ fun assertExists_success() {
+ val assertion = LayoutElementAssertion(ELEMENT_DESCRIPTION, Box.Builder().build())
+
+ assertion.assertExists() // no error
+ }
+
+ @Test
+ fun assertExists_error() {
+ val assertion = LayoutElementAssertion(ELEMENT_DESCRIPTION, null)
+
+ val assertionError = assertThrows(AssertionError::class.java) { assertion.assertExists() }
+
+ assertThat(assertionError)
+ .hasMessageThat()
+ .isEqualTo("Expected $ELEMENT_DESCRIPTION to exist, but it does not.")
+ }
+
+ @Test
+ fun assertDoesNotExist_success() {
+ val assertion = LayoutElementAssertion(ELEMENT_DESCRIPTION, null)
+
+ assertion.assertDoesNotExist() // no error
+ }
+
+ @Test
+ fun assertDoesNotExist_error() {
+ val assertion = LayoutElementAssertion(ELEMENT_DESCRIPTION, Box.Builder().build())
+
+ val assertionError =
+ assertThrows(AssertionError::class.java) { assertion.assertDoesNotExist() }
+
+ assertThat(assertionError)
+ .hasMessageThat()
+ .isEqualTo("Expected $ELEMENT_DESCRIPTION to not exist, but it does.")
+ }
+
+ @Test
+ fun assert_withMatcher_success() {
+ val assertion = LayoutElementAssertion(ELEMENT_DESCRIPTION, Box.Builder().build())
+ assertion.assert(LayoutElementMatcher("Element type is Box") { it is Box })
+ }
+
+ @Test
+ fun assert_withMatcher_error() {
+ val assertion = LayoutElementAssertion(ELEMENT_DESCRIPTION, Box.Builder().build())
+ val matcher = LayoutElementMatcher("Element type is Text") { it is Text }
+
+ val assertionError = assertThrows(AssertionError::class.java) { assertion.assert(matcher) }
+
+ assertThat(assertionError)
+ .hasMessageThat()
+ .isEqualTo(
+ "Expected $ELEMENT_DESCRIPTION to match '${matcher.description}'," +
+ " but it does not."
+ )
+ }
+
+ @Test
+ fun chainAssertions() {
+ val textContent = "testing text"
+ val assertion =
+ LayoutElementAssertion(ELEMENT_DESCRIPTION, Text.Builder().setText(textContent).build())
+ val typeMatcher = LayoutElementMatcher("Element type is Text") { it is Text }
+ val contentMatcher =
+ LayoutElementMatcher("Element text = '$textContent'") {
+ it is Text && it.text?.value == textContent
+ }
+
+ assertion.assert(typeMatcher).assert(contentMatcher)
+ }
+
+ @Test
+ fun chainAssertions_failureInFirst() {
+ val textContent = "testing text"
+ val assertion =
+ LayoutElementAssertion(ELEMENT_DESCRIPTION, Text.Builder().setText(textContent).build())
+ val firstMatcher = LayoutElementMatcher("Element type is Box") { it is Box }
+ val secondMatcher = LayoutElementMatcher("Element type is Text") { it is Text }
+
+ val assertionError =
+ assertThrows(AssertionError::class.java) {
+ assertion.assert(firstMatcher).assert(secondMatcher)
+ }
+
+ assertThat(assertionError)
+ .hasMessageThat()
+ .isEqualTo(
+ "Expected $ELEMENT_DESCRIPTION to match " +
+ "'${firstMatcher.description}', " +
+ "but it does not."
+ )
+ }
+
+ @Test
+ fun chainAssertions_failureInSecond() {
+ val textContent = "testing text"
+ val assertion =
+ LayoutElementAssertion(ELEMENT_DESCRIPTION, Text.Builder().setText(textContent).build())
+ val firstMatcher = LayoutElementMatcher("Element type is Text") { it is Text }
+ val secondMatcher = LayoutElementMatcher("Element type is Box") { it is Box }
+
+ val assertionError =
+ assertThrows(AssertionError::class.java) {
+ assertion.assert(firstMatcher).assert(secondMatcher)
+ }
+
+ assertThat(assertionError)
+ .hasMessageThat()
+ .isEqualTo(
+ "Expected $ELEMENT_DESCRIPTION to match " +
+ "'${secondMatcher.description}', " +
+ "but it does not."
+ )
+ }
+
+ companion object {
+ const val ELEMENT_DESCRIPTION = "testing element"
+ }
+}
diff --git a/wear/protolayout/protolayout-testing/src/test/java/androidx/wear/protolayout/testing/LayoutElementAssertionsProviderTest.kt b/wear/protolayout/protolayout-testing/src/test/java/androidx/wear/protolayout/testing/LayoutElementAssertionsProviderTest.kt
new file mode 100644
index 0000000..036ef7d
--- /dev/null
+++ b/wear/protolayout/protolayout-testing/src/test/java/androidx/wear/protolayout/testing/LayoutElementAssertionsProviderTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2024 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.wear.protolayout.testing
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.wear.protolayout.LayoutElementBuilders.Box
+import androidx.wear.protolayout.LayoutElementBuilders.Column
+import androidx.wear.protolayout.LayoutElementBuilders.Image
+import androidx.wear.protolayout.LayoutElementBuilders.Layout
+import androidx.wear.protolayout.LayoutElementBuilders.Row
+import androidx.wear.protolayout.LayoutElementBuilders.Text
+import com.google.common.truth.ExpectFailure
+import com.google.common.truth.Truth.assertThat
+import junit.framework.TestCase.assertTrue
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.internal.DoNotInstrument
+
+@RunWith(AndroidJUnit4::class)
+@DoNotInstrument
+class LayoutElementAssertionsProviderTest {
+
+ @Test
+ fun primaryConstructor_onRoot() {
+ assertTrue(LayoutElementAssertionsProvider(TEST_LAYOUT.root!!).onRoot().element is Box)
+ }
+
+ @Test
+ fun secondaryConstructor_onRoot() {
+ assertTrue(LayoutElementAssertionsProvider(TEST_LAYOUT).onRoot().element is Box)
+ }
+
+ @Test
+ fun onRoot_description() {
+ val assertionError =
+ assertThrows(AssertionError::class.java) {
+ LayoutElementAssertionsProvider(TEST_LAYOUT).onRoot().assertDoesNotExist()
+ }
+
+ val rootDescription = "root"
+
+ ExpectFailure.assertThat(assertionError)
+ .hasMessageThat()
+ .isEqualTo("Expected $rootDescription to not exist, but it does.")
+ }
+
+ @Test
+ fun onElement_isImage() {
+ val firstImageElement =
+ LayoutElementAssertionsProvider(TEST_LAYOUT).onElement(isImage).element as Image
+ assertThat(firstImageElement.resourceId!!.value).isEqualTo("image1")
+ }
+
+ @Test
+ fun onElement_isText() {
+ val firstTextElement =
+ LayoutElementAssertionsProvider(TEST_LAYOUT).onElement(isText).element as Text
+ assertThat(firstTextElement.text!!.value).isEqualTo("text1")
+ }
+
+ @Test
+ fun onElement_description() {
+ val assertionError =
+ assertThrows(AssertionError::class.java) {
+ LayoutElementAssertionsProvider(TEST_LAYOUT).onElement(isText).assertDoesNotExist()
+ }
+
+ val elementDescription = "element matching '${isText.description}'"
+
+ ExpectFailure.assertThat(assertionError)
+ .hasMessageThat()
+ .isEqualTo("Expected $elementDescription to not exist, but it does.")
+ }
+
+ companion object {
+ val isBox = LayoutElementMatcher("Element type is Box") { it is Box }
+ val isImage = LayoutElementMatcher("Element type is Image") { it is Image }
+ val isText = LayoutElementMatcher("Element type is Text") { it is Text }
+ val TEST_LAYOUT =
+ Layout.Builder()
+ .setRoot(
+ Box.Builder()
+ .addContent(
+ Row.Builder()
+ .addContent(Image.Builder().setResourceId("image1").build())
+ .addContent(Image.Builder().setResourceId("image2").build())
+ .build()
+ )
+ .addContent(
+ Column.Builder()
+ .addContent(Text.Builder().setText("text1").build())
+ .addContent(Text.Builder().setText("text2").build())
+ .build()
+ )
+ .build()
+ )
+ .build()
+ }
+}
diff --git a/wear/protolayout/protolayout/api/current.txt b/wear/protolayout/protolayout/api/current.txt
index 7ef6729..034cbf0 100644
--- a/wear/protolayout/protolayout/api/current.txt
+++ b/wear/protolayout/protolayout/api/current.txt
@@ -558,6 +558,42 @@
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp.Builder setValue(int);
}
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public static final class LayoutElementBuilders.DashedArcLine implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+ method public androidx.wear.protolayout.LayoutElementBuilders.ArcDirectionProp? getArcDirection();
+ method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+ method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicLength();
+ method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
+ method public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern? getLinePattern();
+ method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+ method @Dimension(unit=androidx.annotation.Dimension.DP) public float getThickness();
+ }
+
+ public static final class LayoutElementBuilders.DashedArcLine.Builder {
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public LayoutElementBuilders.DashedArcLine.Builder();
+ method public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine build();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setArcDirection(androidx.wear.protolayout.LayoutElementBuilders.ArcDirectionProp);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setArcDirection(int);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setLayoutConstraintsForDynamicLength(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setLinePattern(androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setThickness(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+ }
+
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public static final class LayoutElementBuilders.DashedLinePattern {
+ method public java.util.List<androidx.wear.protolayout.DimensionBuilders.DegreesProp!> getGapLocations();
+ method public androidx.wear.protolayout.DimensionBuilders.DpProp? getGapSize();
+ }
+
+ public static final class LayoutElementBuilders.DashedLinePattern.Builder {
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public LayoutElementBuilders.DashedLinePattern.Builder();
+ method public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern build();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern.Builder setGapInterval(float);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern.Builder setGapLocations(float...);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern.Builder setGapSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+ }
+
@SuppressCompatibility @androidx.wear.protolayout.expression.ExperimentalProtoLayoutExtensionApi @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class LayoutElementBuilders.ExtensionLayoutElement implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
method public String getExtensionId();
method public androidx.wear.protolayout.DimensionBuilders.ExtensionDimension? getHeight();
diff --git a/wear/protolayout/protolayout/api/restricted_current.txt b/wear/protolayout/protolayout/api/restricted_current.txt
index 7ef6729..034cbf0 100644
--- a/wear/protolayout/protolayout/api/restricted_current.txt
+++ b/wear/protolayout/protolayout/api/restricted_current.txt
@@ -558,6 +558,42 @@
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp.Builder setValue(int);
}
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public static final class LayoutElementBuilders.DashedArcLine implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+ method public androidx.wear.protolayout.LayoutElementBuilders.ArcDirectionProp? getArcDirection();
+ method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+ method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicLength();
+ method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
+ method public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern? getLinePattern();
+ method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+ method @Dimension(unit=androidx.annotation.Dimension.DP) public float getThickness();
+ }
+
+ public static final class LayoutElementBuilders.DashedArcLine.Builder {
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public LayoutElementBuilders.DashedArcLine.Builder();
+ method public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine build();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setArcDirection(androidx.wear.protolayout.LayoutElementBuilders.ArcDirectionProp);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setArcDirection(int);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setLayoutConstraintsForDynamicLength(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setLinePattern(androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setThickness(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+ }
+
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public static final class LayoutElementBuilders.DashedLinePattern {
+ method public java.util.List<androidx.wear.protolayout.DimensionBuilders.DegreesProp!> getGapLocations();
+ method public androidx.wear.protolayout.DimensionBuilders.DpProp? getGapSize();
+ }
+
+ public static final class LayoutElementBuilders.DashedLinePattern.Builder {
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public LayoutElementBuilders.DashedLinePattern.Builder();
+ method public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern build();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern.Builder setGapInterval(float);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern.Builder setGapLocations(float...);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern.Builder setGapSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+ }
+
@SuppressCompatibility @androidx.wear.protolayout.expression.ExperimentalProtoLayoutExtensionApi @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class LayoutElementBuilders.ExtensionLayoutElement implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
method public String getExtensionId();
method public androidx.wear.protolayout.DimensionBuilders.ExtensionDimension? getHeight();
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
index 7c4af90..8fbf8f0 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
@@ -16,6 +16,9 @@
package androidx.wear.protolayout;
+import static androidx.annotation.Dimension.DP;
+import static androidx.wear.protolayout.DimensionBuilders.degrees;
+import static androidx.wear.protolayout.DimensionBuilders.dp;
import static androidx.wear.protolayout.DimensionBuilders.sp;
import static androidx.wear.protolayout.expression.Preconditions.checkNotNull;
@@ -5170,6 +5173,457 @@
}
}
+ /**
+ * A dashed arc line that can be placed in an {@link Arc} container. It is an arc line made up
+ * of arc line segments separated by gaps.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ public static final class DashedArcLine implements ArcLayoutElement {
+ private final LayoutElementProto.DashedArcLine mImpl;
+ @Nullable private final Fingerprint mFingerprint;
+
+ DashedArcLine(LayoutElementProto.DashedArcLine impl, @Nullable Fingerprint fingerprint) {
+ this.mImpl = impl;
+ this.mFingerprint = fingerprint;
+ }
+
+ /**
+ * Gets the length of this line in degrees, including gaps.
+ *
+ * <p>When using a dynamic value, make sure to specify the bounding constraints for the
+ * affected layout element through {@code setLayoutConstraintsForDynamicLength
+ * (AngularLayoutConstraint)}, otherwise {@code build()} fails.
+ */
+ @Nullable
+ public DegreesProp getLength() {
+ if (mImpl.hasLength()) {
+ return DegreesProp.fromProto(mImpl.getLength());
+ } else {
+ return null;
+ }
+ }
+
+ /** Gets the thickness of this line. */
+ @Dimension(unit = DP)
+ public float getThickness() {
+ if (mImpl.hasThickness()) {
+ return DpProp.fromProto(mImpl.getThickness()).getValue();
+ } else {
+ return 0;
+ }
+ }
+
+ /** Gets the color of this line. */
+ @Nullable
+ public ColorProp getColor() {
+ if (mImpl.hasColor()) {
+ return ColorProp.fromProto(mImpl.getColor());
+ } else {
+ return null;
+ }
+ }
+
+ /** Gets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element. */
+ @Nullable
+ public ArcModifiers getModifiers() {
+ if (mImpl.hasModifiers()) {
+ return ArcModifiers.fromProto(mImpl.getModifiers());
+ } else {
+ return null;
+ }
+ }
+
+ /** Gets the direction in which this line is drawn. */
+ @Nullable
+ public ArcDirectionProp getArcDirection() {
+ if (mImpl.hasArcDirection()) {
+ return ArcDirectionProp.fromProto(mImpl.getArcDirection());
+ } else {
+ return null;
+ }
+ }
+
+ /** Gets the dashed line pattern which describes how the arc line is segmented by gaps. */
+ @Nullable
+ public DashedLinePattern getLinePattern() {
+ if (mImpl.hasLinePattern()) {
+ return DashedLinePattern.fromProto(mImpl.getLinePattern());
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the bounding constraints for the layout affected by the dynamic value from {@link
+ * #getLength()}.
+ */
+ @Nullable
+ public AngularLayoutConstraint getLayoutConstraintsForDynamicLength() {
+ if (mImpl.hasLength()) {
+ return AngularLayoutConstraint.fromProto(mImpl.getLength());
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Nullable
+ public Fingerprint getFingerprint() {
+ return mFingerprint;
+ }
+
+ /** Creates a new wrapper instance from the proto. */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public static DashedArcLine fromProto(
+ @NonNull LayoutElementProto.DashedArcLine proto,
+ @Nullable Fingerprint fingerprint) {
+ return new DashedArcLine(proto, fingerprint);
+ }
+
+ @NonNull
+ static DashedArcLine fromProto(@NonNull LayoutElementProto.DashedArcLine proto) {
+ return fromProto(proto, null);
+ }
+
+ /** Returns the internal proto instance. */
+ @NonNull
+ LayoutElementProto.DashedArcLine toProto() {
+ return mImpl;
+ }
+
+ @Override
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public LayoutElementProto.ArcLayoutElement toArcLayoutElementProto() {
+ return LayoutElementProto.ArcLayoutElement.newBuilder().setDashedLine(mImpl).build();
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "DashedArcLine{"
+ + "length="
+ + getLength()
+ + ", thickness="
+ + getThickness()
+ + ", color="
+ + getColor()
+ + ", modifiers="
+ + getModifiers()
+ + ", arcDirection="
+ + getArcDirection()
+ + ", linePattern="
+ + getLinePattern()
+ + "}";
+ }
+
+ /** Builder for {@link DashedArcLine}. */
+ @SuppressWarnings("HiddenSuperclass")
+ public static final class Builder implements ArcLayoutElement.Builder {
+ private final LayoutElementProto.DashedArcLine.Builder mImpl =
+ LayoutElementProto.DashedArcLine.newBuilder();
+ private final Fingerprint mFingerprint = new Fingerprint(-1152963772);
+
+ /** Creates an instance of {@link Builder}. */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ public Builder() {}
+
+ /**
+ * Sets the length of this line, in degrees. If not defined, defaults to 0.
+ *
+ * <p>When using a dynamic value, make sure to specify the bounding constraints for the
+ * affected layout element through {@code setLayoutConstraintsForDynamicLength
+ * (AngularLayoutConstraint)} otherwise {@code build()} fails.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ @NonNull
+ public Builder setLength(@NonNull DegreesProp length) {
+ mImpl.mergeLength(length.toProto());
+ mFingerprint.recordPropertyUpdate(
+ 1, checkNotNull(length.getFingerprint()).aggregateValueAsInt());
+ return this;
+ }
+
+ /**
+ * Sets the thickness of this line. If not defined, defaults to 0.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ @NonNull
+ public Builder setThickness(@Dimension(unit = DP) float thickness) {
+ DpProp thicknessProp = dp(thickness);
+ mImpl.setThickness(thicknessProp.toProto());
+ mFingerprint.recordPropertyUpdate(
+ 2, checkNotNull(thicknessProp.getFingerprint()).aggregateValueAsInt());
+ return this;
+ }
+
+ /**
+ * Sets the color of this line.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ @NonNull
+ public Builder setColor(@NonNull ColorProp color) {
+ mImpl.setColor(color.toProto());
+ mFingerprint.recordPropertyUpdate(
+ 3, checkNotNull(color.getFingerprint()).aggregateValueAsInt());
+ return this;
+ }
+
+ /**
+ * Sets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ @NonNull
+ public Builder setModifiers(@NonNull ArcModifiers modifiers) {
+ mImpl.setModifiers(modifiers.toProto());
+ mFingerprint.recordPropertyUpdate(
+ 4, checkNotNull(modifiers.getFingerprint()).aggregateValueAsInt());
+ return this;
+ }
+
+ /**
+ * Sets the direction in which this line is drawn. If not set, defaults to
+ * ARC_DIRECTION_CLOCKWISE.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ @NonNull
+ public Builder setArcDirection(@NonNull ArcDirectionProp arcDirection) {
+ mImpl.setArcDirection(arcDirection.toProto());
+ mFingerprint.recordPropertyUpdate(
+ 5, checkNotNull(arcDirection.getFingerprint()).aggregateValueAsInt());
+ return this;
+ }
+
+ /**
+ * Sets the direction in which this line is drawn. If not set, defaults to
+ * ARC_DIRECTION_CLOCKWISE.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ @NonNull
+ public Builder setArcDirection(@ArcDirection int arcDirection) {
+ return setArcDirection(
+ new ArcDirectionProp.Builder().setValue(arcDirection).build());
+ }
+
+ /**
+ * Sets the dashed line pattern which describes how the arc line is segmented by gaps.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ @NonNull
+ public Builder setLinePattern(@NonNull DashedLinePattern linePattern) {
+ mImpl.setLinePattern(linePattern.toProto());
+ mFingerprint.recordPropertyUpdate(
+ 6, checkNotNull(linePattern.getFingerprint()).aggregateValueAsInt());
+ return this;
+ }
+ /**
+ * Sets the bounding constraints for the layout affected by the dynamic value from
+ * {@link #setLength(DegreesProp)}.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ @NonNull
+ public Builder setLayoutConstraintsForDynamicLength(
+ @NonNull AngularLayoutConstraint angularLayoutConstraint) {
+ mImpl.mergeLength(angularLayoutConstraint.toProto());
+ mFingerprint.recordPropertyUpdate(
+ 1,
+ checkNotNull(angularLayoutConstraint.getFingerprint())
+ .aggregateValueAsInt());
+ return this;
+ }
+
+ /** Builds an instance with the values accumulated in this Builder. */
+ @SuppressLint("ProtoLayoutMinSchema")
+ @Override
+ @NonNull
+ public DashedArcLine build() {
+ DimensionProto.DegreesProp length = mImpl.getLength();
+ if (length.hasDynamicValue() && !length.hasValueForLayout()) {
+ throw new IllegalStateException(
+ "length with dynamic value requires "
+ + "layoutConstraintsForDynamicLength to be present.");
+ }
+
+ return new DashedArcLine(mImpl.build(), mFingerprint);
+ }
+ }
+ }
+
+ /** A dashed line pattern which describes how the dashed arc line is segmented by gaps. */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ public static final class DashedLinePattern {
+ private final LayoutElementProto.DashedLinePattern mImpl;
+ @Nullable private final Fingerprint mFingerprint;
+
+ DashedLinePattern(
+ LayoutElementProto.DashedLinePattern impl, @Nullable Fingerprint fingerprint) {
+ this.mImpl = impl;
+ this.mFingerprint = fingerprint;
+ }
+
+ /** Gets the size in dp of the gap between the arc line segments. */
+ @Nullable
+ public DpProp getGapSize() {
+ if (mImpl.hasGapSize()) {
+ return DpProp.fromProto(mImpl.getGapSize());
+ } else {
+ return null;
+ }
+ }
+
+ /** Gets the list of each gap's center location in degrees. */
+ @NonNull
+ public List<DegreesProp> getGapLocations() {
+ List<DegreesProp> list = new ArrayList<>();
+ for (DimensionProto.DegreesProp item : mImpl.getGapLocationsList()) {
+ list.add(DegreesProp.fromProto(item));
+ }
+ return Collections.unmodifiableList(list);
+ }
+
+ /** Get the fingerprint for this object, or null if unknown. */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Nullable
+ public Fingerprint getFingerprint() {
+ return mFingerprint;
+ }
+
+ /** Creates a new wrapper instance from the proto. */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public static DashedLinePattern fromProto(
+ @NonNull LayoutElementProto.DashedLinePattern proto,
+ @Nullable Fingerprint fingerprint) {
+ return new DashedLinePattern(proto, fingerprint);
+ }
+
+ @NonNull
+ static DashedLinePattern fromProto(@NonNull LayoutElementProto.DashedLinePattern proto) {
+ return fromProto(proto, null);
+ }
+
+ /** Returns the internal proto instance. */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public LayoutElementProto.DashedLinePattern toProto() {
+ return mImpl;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "DashedLinePattern{"
+ + "gapSize="
+ + getGapSize()
+ + ", gapLocations="
+ + getGapLocations()
+ + "}";
+ }
+
+ /** Builder for {@link DashedLinePattern} */
+ public static final class Builder {
+ private final LayoutElementProto.DashedLinePattern.Builder mImpl =
+ LayoutElementProto.DashedLinePattern.newBuilder();
+ private final Fingerprint mFingerprint = new Fingerprint(1050989205);
+
+ /** Creates an instance of {@link Builder}. */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ public Builder() {}
+
+ /**
+ * Sets the size in dp of the gap between the segments. If not defined, defaults to 0.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ @NonNull
+ public Builder setGapSize(@Dimension(unit = DP) float gapSizeInDp) {
+ DpProp gapSizeProp = dp(gapSizeInDp);
+ mImpl.setGapSize(gapSizeProp.toProto());
+ mFingerprint.recordPropertyUpdate(
+ 1, checkNotNull(gapSizeProp.getFingerprint()).aggregateValueAsInt());
+ return this;
+ }
+
+ /**
+ * Adds one item to the list of each gap's center location in degrees.
+ *
+ * <p>Note that this field only supports static values.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ @NonNull
+ private Builder addGapLocation(@NonNull DegreesProp gapLocation) {
+ if (gapLocation.getDynamicValue() != null) {
+ throw new IllegalArgumentException(
+ "DashedLinePattern.Builder.addGapLocation doesn't support dynamic "
+ + "values.");
+ }
+ mImpl.addGapLocations(gapLocation.toProto());
+ mFingerprint.recordPropertyUpdate(
+ 2, checkNotNull(gapLocation.getFingerprint()).aggregateValueAsInt());
+ return this;
+ }
+
+ /**
+ * Sets the list of each gap's center location in degrees.
+ *
+ * <p>The interval between any two locations could not be shorter than thickness plus
+ * gap size.
+ *
+ * <p>Note that calling this method will invalidate the previous call of {@link
+ * #setGapInterval}
+ */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ @NonNull
+ public Builder setGapLocations(@NonNull float... gapLocationsInDegrees) {
+ mImpl.clearGapLocations();
+
+ for (float gapLocation: gapLocationsInDegrees) {
+ addGapLocation(degrees(gapLocation));
+ }
+
+ return this;
+ }
+
+ /**
+ * Sets the interval length in degrees between two consecutive gap center locations. The
+ * arc line will have arc line segments with equal length.
+ *
+ * <p>The interval could not be shorter than thickness plus gap size.
+ *
+ * <p>Note that calling this method will remove all the gap locations set previously
+ * with {@link #setGapLocations}
+ */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setGapInterval(float gapIntervalInDegrees) {
+ mImpl.clearGapLocations();
+
+ float gapLocation = gapIntervalInDegrees;
+ while (gapLocation <= 360F) {
+ addGapLocation(degrees(gapLocation));
+ gapLocation += gapIntervalInDegrees;
+ }
+
+ return this;
+ }
+
+ private static final int GAP_COUNTS_LIMIT = 100;
+
+ /** Builds an instance from accumulated values. */
+ @NonNull
+ public DashedLinePattern build() {
+ if (mImpl.getGapLocationsList().size() > GAP_COUNTS_LIMIT) {
+ throw new IllegalArgumentException(
+ "Number of gaps can't be larger than " + GAP_COUNTS_LIMIT + ".");
+ }
+ return new DashedLinePattern(mImpl.build(), mFingerprint);
+ }
+ }
+ }
+
+
/** A simple spacer used to provide padding between adjacent elements in an {@link Arc}. */
@RequiresSchemaVersion(major = 1, minor = 0)
public static final class ArcSpacer implements ArcLayoutElement {
@@ -5871,6 +6325,9 @@
if (proto.hasAdapter()) {
return ArcAdapter.fromProto(proto.getAdapter(), fingerprint);
}
+ if (proto.hasDashedLine()) {
+ return DashedArcLine.fromProto(proto.getDashedLine(), fingerprint);
+ }
throw new IllegalStateException("Proto was not a recognised instance of ArcLayoutElement");
}
diff --git a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
index 1653411..5605c8b 100644
--- a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
+++ b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
@@ -21,6 +21,8 @@
import static androidx.wear.protolayout.DimensionBuilders.expand;
import static androidx.wear.protolayout.DimensionBuilders.sp;
import static androidx.wear.protolayout.DimensionBuilders.weight;
+import static androidx.wear.protolayout.LayoutElementBuilders.ARC_DIRECTION_COUNTER_CLOCKWISE;
+import static androidx.wear.protolayout.LayoutElementBuilders.ARC_DIRECTION_NORMAL;
import static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.ROBOTO_FLEX_FONT;
import static androidx.wear.protolayout.LayoutElementBuilders.TABULAR_OPTION_TAG;
import static androidx.wear.protolayout.LayoutElementBuilders.WEIGHT_AXIS_TAG;
@@ -35,6 +37,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.wear.protolayout.expression.AppDataKey;
import androidx.wear.protolayout.expression.DynamicBuilders;
+import androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine;
+import androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern;
+import androidx.wear.protolayout.proto.ColorProto;
import androidx.wear.protolayout.proto.DimensionProto;
import androidx.wear.protolayout.proto.LayoutElementProto;
import androidx.wear.protolayout.proto.TypesProto;
@@ -467,7 +472,7 @@
@Test
public void testArcs_withSetDirection_correctlySetsValues() {
- int arcLineDirection = LayoutElementBuilders.ARC_DIRECTION_COUNTER_CLOCKWISE;
+ int arcLineDirection = ARC_DIRECTION_COUNTER_CLOCKWISE;
int arcTextDirection = LayoutElementBuilders.ARC_DIRECTION_NORMAL;
int arcDirection = LayoutElementBuilders.ARC_DIRECTION_CLOCKWISE;
@@ -712,4 +717,113 @@
assertThat(fontStyleProto.getPreferredFontFamilies(0)).isEqualTo(expectedFontFamily);
assertThat(fontStyleProto.getPreferredFontFamilies(1)).isEqualTo(fallbackFontFamily);
}
+
+ @Test
+ public void dashedArcLine_length() {
+ DashedArcLine dashedArcLine =
+ new DashedArcLine.Builder()
+ .setLength(DEGREES_PROP)
+ .setLayoutConstraintsForDynamicLength(DEGREES_PROP_CONSTRAINT)
+ .build();
+
+ DimensionProto.DegreesProp lengthProto = dashedArcLine.toProto().getLength();
+
+ assertThat(lengthProto.getValue()).isEqualTo(DEGREES_PROP.getValue());
+ assertThat(lengthProto.getDynamicValue().getStateSource().getSourceKey())
+ .isEqualTo(STATE_KEY);
+ assertThat(lengthProto.getValueForLayout()).isEqualTo(DEGREES_PROP_CONSTRAINT.getValue());
+ assertThat(lengthProto.getAngularAlignmentForLayoutValue())
+ .isEqualTo(DEGREES_PROP_CONSTRAINT.getAngularAlignment());
+ }
+
+ @Test
+ public void dashedArcLine_length_withoutLayoutConstraint_throws() {
+ assertThrows(
+ IllegalStateException.class,
+ () -> new DashedArcLine.Builder().setLength(DEGREES_PROP).build());
+ }
+
+ @Test
+ public void dashedArcLine_thickness() {
+ float thickness = 5F;
+ DashedArcLine dashedArcLine =
+ new DashedArcLine.Builder()
+ .setThickness(thickness)
+ .build();
+
+ assertThat(dashedArcLine.toProto().getThickness().getValue())
+ .isEqualTo(thickness);
+ }
+
+ @Test
+ public void dashedArcLine_color() {
+ String stateKey = "color-key";
+ ColorBuilders.ColorProp color =
+ new ColorBuilders.ColorProp.Builder(Color.BLUE)
+ .setDynamicValue(DynamicBuilders.DynamicColor.from(
+ new AppDataKey<>(stateKey)
+ )).build();
+ DashedArcLine dashedArcLine =
+ new DashedArcLine.Builder()
+ .setColor(color)
+ .build();
+
+ ColorProto.ColorProp colorProto = dashedArcLine.toProto().getColor();
+ assertThat(colorProto.getArgb()).isEqualTo(Color.BLUE);
+ assertThat(colorProto.getDynamicValue().getStateSource().getSourceKey())
+ .isEqualTo(stateKey);
+ }
+
+ @Test
+ public void dashedArcLine_arcDirection() {
+ DashedArcLine dashedArcLine1 =
+ new DashedArcLine.Builder().build();
+ DashedArcLine dashedArcLine2 =
+ new DashedArcLine.Builder()
+ .setArcDirection(ARC_DIRECTION_COUNTER_CLOCKWISE)
+ .build();
+
+ assertThat(dashedArcLine1.toProto().getArcDirection().getValue().getNumber())
+ .isEqualTo(ARC_DIRECTION_NORMAL);
+ assertThat(dashedArcLine2.toProto().getArcDirection().getValue().getNumber())
+ .isEqualTo(ARC_DIRECTION_COUNTER_CLOCKWISE);
+ }
+
+ @Test
+ public void dashedArcLine_brushWithEqualSegments() {
+ DashedArcLine dashedArcLine =
+ new DashedArcLine.Builder()
+ .setLinePattern(
+ new DashedLinePattern.Builder()
+ .setGapSize(4.5F)
+ .setGapInterval(111)
+ .build())
+ .build();
+
+ LayoutElementProto.DashedLinePattern brush = dashedArcLine.getLinePattern().toProto();
+ assertThat(brush.getGapSize().getValue()).isEqualTo(4.5F);
+ List<DimensionProto.DegreesProp> gapLocations =brush.getGapLocationsList();
+ assertThat(gapLocations.get(0).getValue()).isEqualTo(111F);
+ assertThat(gapLocations.get(1).getValue()).isEqualTo(222F);
+ assertThat(gapLocations.get(2).getValue()).isEqualTo(333F);
+ }
+
+ @Test
+ public void dashedArcLine_brushWithNonEqualSegments() {
+ DashedArcLine dashedArcLine =
+ new DashedArcLine.Builder()
+ .setLinePattern(
+ new DashedLinePattern.Builder()
+ .setGapSize(4.5F)
+ .setGapLocations(66F, 111F, 321F, 212F).build())
+ .build();
+
+ LayoutElementProto.DashedLinePattern brush = dashedArcLine.getLinePattern().toProto();
+ assertThat(brush.getGapSize().getValue()).isEqualTo(4.5F);
+ List<DimensionProto.DegreesProp> gapLocations =brush.getGapLocationsList();
+ assertThat(gapLocations.get(0).getValue()).isEqualTo(66F);
+ assertThat(gapLocations.get(1).getValue()).isEqualTo(111F);
+ assertThat(gapLocations.get(2).getValue()).isEqualTo(321F);
+ assertThat(gapLocations.get(3).getValue()).isEqualTo(212F);
+ }
}