Merge "Allow querying isLookingAhead from MeasureScope" into androidx-main
diff --git a/.github/ci-control/ci-config.json b/.github/ci-control/ci-config.json
index 06a5ee1..2d07c46 100644
--- a/.github/ci-control/ci-config.json
+++ b/.github/ci-control/ci-config.json
@@ -7,7 +7,6 @@
],
"compose" : {
"include" : [
- "compose-runtime"
],
"default": false
},
diff --git a/activity/integration-tests/testapp/build.gradle b/activity/integration-tests/testapp/build.gradle
index 7799f8e..f7680a9c 100644
--- a/activity/integration-tests/testapp/build.gradle
+++ b/activity/integration-tests/testapp/build.gradle
@@ -29,8 +29,7 @@
dependencies {
implementation(project(":activity:activity-ktx"))
- implementation(projectOrArtifact(":lifecycle:lifecycle-runtime-ktx"))
- androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-common"))
+ implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
implementation("androidx.appcompat:appcompat:1.6.0")
implementation("androidx.core:core-splashscreen:1.0.0")
androidTestImplementation(libs.kotlinStdlib)
diff --git a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateCall.kt b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateCall.kt
index 5b1fdc6..a58ae85 100644
--- a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateCall.kt
+++ b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateCall.kt
@@ -20,8 +20,8 @@
import androidx.appactions.builtintypes.experimental.types.Call
import androidx.appactions.builtintypes.experimental.types.GenericErrorStatus
import androidx.appactions.builtintypes.experimental.types.SuccessStatus
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.CapabilityFactory
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter
@@ -38,21 +38,31 @@
private const val CAPABILITY_NAME: String = "actions.intent.CREATE_CALL"
+@Suppress("UNCHECKED_CAST")
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(CreateCall.Properties::class.java)
.setArguments(CreateCall.Arguments::class.java, CreateCall.Arguments::Builder)
.setOutput(CreateCall.Output::class.java)
.bindOptionalParameter(
"call.callFormat",
- { property -> Optional.ofNullable(property.callFormat) },
+ { properties ->
+ Optional.ofNullable(
+ properties[CreateCall.PropertyMapStrings.CALL_FORMAT.key]
+ as Property<Call.CanonicalValue.CallFormat>
+ )
+ },
CreateCall.Arguments.Builder::setCallFormat,
TypeConverters.CALL_FORMAT_PARAM_VALUE_CONVERTER,
TypeConverters.CALL_FORMAT_ENTITY_CONVERTER
)
.bindRepeatedParameter(
"call.participant",
- { property -> Optional.ofNullable(property.participant) },
+ { properties ->
+ Optional.ofNullable(
+ properties[CreateCall.PropertyMapStrings.PARTICIPANT.key]
+ as Property<Participant>
+ )
+ },
CreateCall.Arguments.Builder::setParticipantList,
ParticipantValue.PARAM_VALUE_CONVERTER,
EntityConverter.of(PARTICIPANT_TYPE_SPEC)
@@ -71,57 +81,26 @@
@CapabilityFactory(name = CAPABILITY_NAME)
class CreateCall private constructor() {
- class CapabilityBuilder :
- Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
- >(ACTION_SPEC) {
- override fun build(): Capability {
- super.setProperty(Properties.Builder().build())
- // TODO(b/268369632): No-op remove empty property builder after Property is removed.
- super.setProperty(Properties.Builder().build())
- return super.build()
- }
+ internal enum class PropertyMapStrings(val key: String) {
+ CALL_FORMAT("call.callFormat"),
+ PARTICIPANT("call.participant"),
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties
- internal constructor(
- val callFormat: Property<Call.CanonicalValue.CallFormat>?,
- val participant: Property<Participant>?
- ) {
- override fun toString(): String {
- return "Property(callFormat=$callFormat, participant=$participant)"
- }
+ class CapabilityBuilder :
+ Capability.Builder<
+ CapabilityBuilder, Arguments, Output, Confirmation, ExecutionSession
+ >(ACTION_SPEC) {
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
+ private var properties = mutableMapOf<String, Property<*>>()
- other as Properties
+ fun setCallFormat(callFormat: Property<Call.CanonicalValue.CallFormat>): CapabilityBuilder =
+ apply {
+ properties[PropertyMapStrings.CALL_FORMAT.key] = callFormat
+ }
- if (callFormat != other.callFormat) return false
- if (participant != other.participant) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- var result = callFormat.hashCode()
- result = 31 * result + participant.hashCode()
- return result
- }
-
- class Builder {
- private var callFormat: Property<Call.CanonicalValue.CallFormat>? = null
-
- private var participant: Property<Participant>? = null
-
- fun setCallFormat(callFormat: Property<Call.CanonicalValue.CallFormat>): Builder =
- apply {
- this.callFormat = callFormat
- }
-
- fun build(): Properties = Properties(callFormat, participant)
+ override fun build(): Capability {
+ super.setProperty(properties)
+ return super.build()
}
}
diff --git a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateMessage.kt b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateMessage.kt
index d2b8b20..806e5ac 100644
--- a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateMessage.kt
+++ b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateMessage.kt
@@ -18,11 +18,10 @@
import androidx.appactions.builtintypes.experimental.properties.Recipient
import androidx.appactions.builtintypes.experimental.types.GenericErrorStatus
-import androidx.appactions.builtintypes.experimental.types.SuccessStatus
import androidx.appactions.builtintypes.experimental.types.Message
-
-import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.builtintypes.experimental.types.SuccessStatus
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.CapabilityFactory
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter
@@ -31,8 +30,8 @@
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters.MESSAGE_TYPE_SPEC
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters.RECIPIENT_TYPE_SPEC
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.StringValue
import androidx.appactions.interaction.capabilities.core.properties.Property
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
import androidx.appactions.interaction.proto.ParamValue
import androidx.appactions.interaction.protobuf.Struct
import androidx.appactions.interaction.protobuf.Value
@@ -40,21 +39,31 @@
private const val CAPABILITY_NAME: String = "actions.intent.CREATE_MESSAGE"
+@Suppress("UNCHECKED_CAST")
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(CreateMessage.Properties::class.java)
.setArguments(CreateMessage.Arguments::class.java, CreateMessage.Arguments::Builder)
.setOutput(CreateMessage.Output::class.java)
.bindRepeatedParameter(
"message.recipient",
- { property -> Optional.ofNullable(property.recipient) },
+ { properties ->
+ Optional.ofNullable(
+ properties[CreateMessage.PropertyMapStrings.RECIPIENT.key]
+ as Property<Recipient>
+ )
+ },
CreateMessage.Arguments.Builder::setRecipientList,
RecipientValue.PARAM_VALUE_CONVERTER,
EntityConverter.of(RECIPIENT_TYPE_SPEC)
)
.bindOptionalParameter(
"message.text",
- { property -> Optional.ofNullable(property.messageText) },
+ { properties ->
+ Optional.ofNullable(
+ properties[CreateMessage.PropertyMapStrings.MESSAGE_TEXT.key]
+ as Property<StringValue>
+ )
+ },
CreateMessage.Arguments.Builder::setMessageText,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER
@@ -73,59 +82,29 @@
@CapabilityFactory(name = CAPABILITY_NAME)
class CreateMessage private constructor() {
- class CapabilityBuilder :
- Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
- >(ACTION_SPEC) {
- override fun build(): Capability {
- super.setProperty(Properties.Builder().build())
- // TODO(b/268369632): No-op remove empty property builder after Property is removed.
- super.setProperty(Properties.Builder().build())
- return super.build()
- }
+ internal enum class PropertyMapStrings(val key: String) {
+ MESSAGE_TEXT("message.text"),
+ RECIPIENT("message.recipient"),
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties
- internal constructor(
- val recipient: Property<Recipient>?,
- val messageText: Property<StringValue>?
- ) {
- override fun toString(): String {
- return "Property(recipient=$recipient, messageText=$messageText)"
+ class CapabilityBuilder :
+ Capability.Builder<
+ CapabilityBuilder, Arguments, Output, Confirmation, ExecutionSession
+ >(ACTION_SPEC) {
+
+ private var properties = mutableMapOf<String, Property<*>>()
+
+ fun setMessageText(messageText: Property<StringValue>): CapabilityBuilder = apply {
+ properties[PropertyMapStrings.MESSAGE_TEXT.key] = messageText
}
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as Properties
-
- if (recipient != other.recipient) return false
- if (messageText != other.messageText) return false
-
- return true
+ fun setRecipient(recipient: Property<Recipient>): CapabilityBuilder = apply {
+ properties[PropertyMapStrings.RECIPIENT.key] = recipient
}
- override fun hashCode(): Int {
- var result = recipient.hashCode()
- result = 31 * result + messageText.hashCode()
- return result
- }
-
- class Builder {
- private var recipient: Property<Recipient>? = null
- private var messageText: Property<StringValue>? = null
-
- fun setRecipient(recipient: Property<Recipient>): Builder = apply {
- this.recipient = recipient
- }
-
- fun setMessageText(messageText: Property<StringValue>): Builder = apply {
- this.messageText = messageText
- }
-
- fun build(): Properties = Properties(recipient, messageText)
+ override fun build(): Capability {
+ super.setProperty(properties)
+ return super.build()
}
}
diff --git a/appactions/interaction/interaction-capabilities-core/api/current.txt b/appactions/interaction/interaction-capabilities-core/api/current.txt
index 7622cc1..b2e08cf 100644
--- a/appactions/interaction/interaction-capabilities-core/api/current.txt
+++ b/appactions/interaction/interaction-capabilities-core/api/current.txt
@@ -13,7 +13,7 @@
property public String id;
}
- public abstract static class Capability.Builder<BuilderT extends androidx.appactions.interaction.capabilities.core.Capability.Builder<BuilderT, PropertyT, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT>, PropertyT, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT extends androidx.appactions.interaction.capabilities.core.BaseExecutionSession<ArgumentsT, OutputT>> {
+ public abstract static class Capability.Builder<BuilderT extends androidx.appactions.interaction.capabilities.core.Capability.Builder<BuilderT, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT>, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT extends androidx.appactions.interaction.capabilities.core.BaseExecutionSession<ArgumentsT, OutputT>> {
method public final BuilderT asBuilder();
method public androidx.appactions.interaction.capabilities.core.Capability build();
method public final BuilderT setExecutionCallback(androidx.appactions.interaction.capabilities.core.ExecutionCallback<ArgumentsT,OutputT> executionCallback);
diff --git a/appactions/interaction/interaction-capabilities-core/api/public_plus_experimental_current.txt b/appactions/interaction/interaction-capabilities-core/api/public_plus_experimental_current.txt
index 7622cc1..b2e08cf 100644
--- a/appactions/interaction/interaction-capabilities-core/api/public_plus_experimental_current.txt
+++ b/appactions/interaction/interaction-capabilities-core/api/public_plus_experimental_current.txt
@@ -13,7 +13,7 @@
property public String id;
}
- public abstract static class Capability.Builder<BuilderT extends androidx.appactions.interaction.capabilities.core.Capability.Builder<BuilderT, PropertyT, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT>, PropertyT, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT extends androidx.appactions.interaction.capabilities.core.BaseExecutionSession<ArgumentsT, OutputT>> {
+ public abstract static class Capability.Builder<BuilderT extends androidx.appactions.interaction.capabilities.core.Capability.Builder<BuilderT, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT>, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT extends androidx.appactions.interaction.capabilities.core.BaseExecutionSession<ArgumentsT, OutputT>> {
method public final BuilderT asBuilder();
method public androidx.appactions.interaction.capabilities.core.Capability build();
method public final BuilderT setExecutionCallback(androidx.appactions.interaction.capabilities.core.ExecutionCallback<ArgumentsT,OutputT> executionCallback);
diff --git a/appactions/interaction/interaction-capabilities-core/api/restricted_current.txt b/appactions/interaction/interaction-capabilities-core/api/restricted_current.txt
index 7622cc1..b2e08cf 100644
--- a/appactions/interaction/interaction-capabilities-core/api/restricted_current.txt
+++ b/appactions/interaction/interaction-capabilities-core/api/restricted_current.txt
@@ -13,7 +13,7 @@
property public String id;
}
- public abstract static class Capability.Builder<BuilderT extends androidx.appactions.interaction.capabilities.core.Capability.Builder<BuilderT, PropertyT, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT>, PropertyT, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT extends androidx.appactions.interaction.capabilities.core.BaseExecutionSession<ArgumentsT, OutputT>> {
+ public abstract static class Capability.Builder<BuilderT extends androidx.appactions.interaction.capabilities.core.Capability.Builder<BuilderT, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT>, ArgumentsT, OutputT, ConfirmationT, ExecutionSessionT extends androidx.appactions.interaction.capabilities.core.BaseExecutionSession<ArgumentsT, OutputT>> {
method public final BuilderT asBuilder();
method public androidx.appactions.interaction.capabilities.core.Capability build();
method public final BuilderT setExecutionCallback(androidx.appactions.interaction.capabilities.core.ExecutionCallback<ArgumentsT,OutputT> executionCallback);
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/Capability.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/Capability.kt
index a043cd2..6fc55f2 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/Capability.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/Capability.kt
@@ -20,9 +20,10 @@
import androidx.appactions.interaction.capabilities.core.impl.CapabilitySession
import androidx.appactions.interaction.capabilities.core.impl.SingleTurnCapabilityImpl
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
+import androidx.appactions.interaction.capabilities.core.impl.task.EmptyTaskUpdater
import androidx.appactions.interaction.capabilities.core.impl.task.SessionBridge
import androidx.appactions.interaction.capabilities.core.impl.task.TaskCapabilityImpl
-import androidx.appactions.interaction.capabilities.core.impl.task.EmptyTaskUpdater
+import androidx.appactions.interaction.capabilities.core.properties.Property
import androidx.appactions.interaction.proto.AppActionsContext.AppAction
/**
@@ -61,27 +62,25 @@
BuilderT :
Builder<
BuilderT,
- PropertyT,
ArgumentsT,
OutputT,
ConfirmationT,
ExecutionSessionT
>,
- PropertyT,
ArgumentsT,
OutputT,
ConfirmationT,
ExecutionSessionT : BaseExecutionSession<ArgumentsT, OutputT>
> private constructor() {
private var id: String? = null
- private var property: PropertyT? = null
+ private var property: Map<String, Property<*>>? = null
private var executionCallback: ExecutionCallback<ArgumentsT, OutputT>? = null
private var sessionFactory:
- (hostProperties: HostProperties?) -> ExecutionSessionT? = { _ -> null }
- private var actionSpec: ActionSpec<PropertyT, ArgumentsT, OutputT>? = null
+ (hostProperties: HostProperties?) -> ExecutionSessionT? = { _ -> null }
+ private var actionSpec: ActionSpec<ArgumentsT, OutputT>? = null
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- constructor(actionSpec: ActionSpec<PropertyT, ArgumentsT, OutputT>) : this() {
+ constructor(actionSpec: ActionSpec<ArgumentsT, OutputT>) : this() {
this.actionSpec = actionSpec
}
@@ -111,7 +110,7 @@
* Sets the Property instance for this capability.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- fun setProperty(property: PropertyT) = asBuilder().apply {
+ fun setProperty(property: Map<String, Property<*>>) = asBuilder().apply {
this.property = property
}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/ErrorStatusInternal.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/ErrorStatusInternal.java
index 17b9eeb..d983859 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/ErrorStatusInternal.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/ErrorStatusInternal.java
@@ -23,13 +23,16 @@
public enum ErrorStatusInternal {
CANCELLED(0),
TIMEOUT(1),
- INVALID_REQUEST_TYPE(2),
+ INVALID_REQUEST(2),
UNCHANGED_DISAMBIG_STATE(3),
INVALID_RESOLVER(4),
STRUCT_CONVERSION_FAILURE(5),
+ // TODO(b/276354491): remove SYNC / CONFIRM / TOUCH_EVENT failure codes
SYNC_REQUEST_FAILURE(6),
CONFIRMATION_REQUEST_FAILURE(7),
- TOUCH_EVENT_REQUEST_FAILURE(8);
+ TOUCH_EVENT_REQUEST_FAILURE(8),
+ EXTERNAL_EXCEPTION(9),
+ SESSION_ALREADY_DESTROYED(10);
private final int mCode;
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityImpl.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityImpl.kt
index 050d370..2a9393e 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityImpl.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityImpl.kt
@@ -17,10 +17,11 @@
package androidx.appactions.interaction.capabilities.core.impl
import androidx.annotation.RestrictTo
-import androidx.appactions.interaction.capabilities.core.ExecutionCallback
import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.interaction.capabilities.core.ExecutionCallback
import androidx.appactions.interaction.capabilities.core.HostProperties
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
+import androidx.appactions.interaction.capabilities.core.properties.Property
import androidx.appactions.interaction.proto.AppActionsContext.AppAction
import androidx.appactions.interaction.proto.TaskInfo
import kotlinx.coroutines.sync.Mutex
@@ -28,13 +29,12 @@
/** @suppress */
@RestrictTo(RestrictTo.Scope.LIBRARY)
internal class SingleTurnCapabilityImpl<
- PropertyT,
ArgumentsT,
OutputT,
> constructor(
id: String,
- val actionSpec: ActionSpec<PropertyT, ArgumentsT, OutputT>,
- val property: PropertyT,
+ val actionSpec: ActionSpec<ArgumentsT, OutputT>,
+ val property: Map<String, Property<*>>,
val executionCallback: ExecutionCallback<ArgumentsT, OutputT>,
) : Capability(id) {
private val mutex = Mutex()
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilitySession.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilitySession.kt
index 0f1bccb..32f3753 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilitySession.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilitySession.kt
@@ -21,6 +21,9 @@
import androidx.appactions.interaction.capabilities.core.ExecutionResult
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
import androidx.appactions.interaction.capabilities.core.impl.utils.invokeExternalSuspendBlock
+import androidx.appactions.interaction.capabilities.core.impl.utils.isCausedBy
+import androidx.appactions.interaction.capabilities.core.impl.utils.toErrorStatusInternal
+import androidx.appactions.interaction.capabilities.core.impl.exceptions.InvalidRequestException
import androidx.appactions.interaction.proto.AppActionsContext.AppDialogState
import androidx.appactions.interaction.proto.FulfillmentResponse
import androidx.appactions.interaction.proto.ParamValue
@@ -42,7 +45,7 @@
OutputT,
>(
override val sessionId: String,
- private val actionSpec: ActionSpec<*, ArgumentsT, OutputT>,
+ private val actionSpec: ActionSpec<ArgumentsT, OutputT>,
private val executionCallback: ExecutionCallback<ArgumentsT, OutputT>,
private val mutex: Mutex,
private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
@@ -81,8 +84,11 @@
}
callback.onSuccess(convertToFulfillmentResponse(output))
} catch (t: Throwable) {
- // TODO(b/276354491) add fine-grained error handling
- callback.onError(ErrorStatusInternal.CANCELLED)
+ callback.onError(t.toErrorStatusInternal())
+ // if the exception is caused by a bad request, do not crash the app
+ if (!t.isCausedBy(InvalidRequestException::class)) {
+ throw t
+ }
} finally {
UiHandleRegistry.unregisterUiHandle(uiHandle)
mutex.unlock(owner = this@SingleTurnCapabilitySession)
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/StructConversionException.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/InvalidRequestException.kt
similarity index 61%
copy from appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/StructConversionException.java
copy to appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/InvalidRequestException.kt
index be13634..b060fc8 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/StructConversionException.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/InvalidRequestException.kt
@@ -14,17 +14,10 @@
* limitations under the License.
*/
-package androidx.appactions.interaction.capabilities.core.impl.exceptions;
+package androidx.appactions.interaction.capabilities.core.impl.exceptions
-import androidx.annotation.Nullable;
-
-/** Represents exceptions that happen during object conversion to/from Struct proto. */
-public class StructConversionException extends Exception {
- public StructConversionException(@Nullable String message) {
- super(message);
- }
-
- public StructConversionException(@Nullable String message, @Nullable Throwable cause) {
- super(message, cause);
- }
+/** Represents exceptions that happen as a result of some incoming request that is invalid. */
+sealed class InvalidRequestException : Exception {
+ constructor(message: String?) : super(message)
+ constructor(message: String?, cause: Throwable?) : super(message, cause)
}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/StructConversionException.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/StructConversionException.kt
similarity index 69%
rename from appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/StructConversionException.java
rename to appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/StructConversionException.kt
index be13634..450c533 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/StructConversionException.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/exceptions/StructConversionException.kt
@@ -14,17 +14,10 @@
* limitations under the License.
*/
-package androidx.appactions.interaction.capabilities.core.impl.exceptions;
-
-import androidx.annotation.Nullable;
+package androidx.appactions.interaction.capabilities.core.impl.exceptions
/** Represents exceptions that happen during object conversion to/from Struct proto. */
-public class StructConversionException extends Exception {
- public StructConversionException(@Nullable String message) {
- super(message);
- }
-
- public StructConversionException(@Nullable String message, @Nullable Throwable cause) {
- super(message, cause);
- }
+class StructConversionException : InvalidRequestException {
+ constructor(message: String?) : super(message)
+ constructor(message: String?, cause: Throwable?) : super(message, cause)
}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpec.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpec.java
index e17f14c..eb8d0ad 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpec.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpec.java
@@ -18,6 +18,7 @@
import androidx.annotation.NonNull;
import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException;
+import androidx.appactions.interaction.capabilities.core.properties.Property;
import androidx.appactions.interaction.proto.AppActionsContext.AppAction;
import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput;
import androidx.appactions.interaction.proto.ParamValue;
@@ -28,15 +29,15 @@
/**
* A specification for an action, describing it from the app's point of view.
*
- * @param <PropertyT> typed description of action's characteristics.
* @param <ArgumentsT> typed representation of action's arguments.
- * @param <OutputT> typed action's execution output.
+ * @param <OutputT> typed action's execution output.
*/
-public interface ActionSpec<PropertyT, ArgumentsT, OutputT> {
+public interface ActionSpec<ArgumentsT, OutputT> {
/** Converts the property to the {@code AppAction} proto. */
@NonNull
- AppAction convertPropertyToProto(PropertyT property);
+ AppAction convertPropertyToProto(@NonNull Map<String,
+ Property<?>> property);
/** Builds this action's arguments from an ArgumentsWrapper instance. */
@NonNull
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.java
index e95f904..df268de 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.java
@@ -40,18 +40,13 @@
/**
* A builder for the {@code ActionSpec}.
- *
- * @param <PropertyT>
- * @param <ArgumentsT>
- * @param <ArgumentsBuilderT>
- * @param <OutputT>
*/
-public final class ActionSpecBuilder<
- PropertyT, ArgumentsT, ArgumentsBuilderT extends BuilderOf<ArgumentsT>, OutputT> {
+public final class ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT extends BuilderOf<ArgumentsT>,
+ OutputT> {
private final String mCapabilityName;
private final Supplier<ArgumentsBuilderT> mArgumentBuilderSupplier;
- private final ArrayList<ParamBinding<PropertyT, ArgumentsT, ArgumentsBuilderT>>
+ private final ArrayList<ParamBinding<ArgumentsT, ArgumentsBuilderT>>
mParamBindingList = new ArrayList<>();
private final Map<String, Function<OutputT, List<ParamValue>>> mOutputBindings =
new HashMap<>();
@@ -67,49 +62,44 @@
* to Object as a placeholder, which must be replaced by calling setArgument.
*/
@NonNull
- public static ActionSpecBuilder<Void, Object, BuilderOf<Object>, Void> ofCapabilityNamed(
+ public static ActionSpecBuilder<Object, BuilderOf<Object>, Void> ofCapabilityNamed(
@NonNull String capabilityName) {
return new ActionSpecBuilder<>(capabilityName, () -> Object::new);
}
/** Sets the property type and returns a new {@code ActionSpecBuilder}. */
- @NonNull
- public <NewPropertyT>
- ActionSpecBuilder<NewPropertyT, ArgumentsT, ArgumentsBuilderT, OutputT> setDescriptor(
- @NonNull Class<NewPropertyT> unused) {
- return new ActionSpecBuilder<>(this.mCapabilityName, this.mArgumentBuilderSupplier);
- }
/** Sets the argument type and its builder and returns a new {@code ActionSpecBuilder}. */
@NonNull
public <NewArgumentsT, NewArgumentsBuilderT extends BuilderOf<NewArgumentsT>>
- ActionSpecBuilder<PropertyT, NewArgumentsT, NewArgumentsBuilderT, OutputT> setArguments(
- @NonNull Class<NewArgumentsT> unused,
- @NonNull Supplier<NewArgumentsBuilderT> argumentBuilderSupplier) {
+ ActionSpecBuilder<NewArgumentsT, NewArgumentsBuilderT, OutputT> setArguments(
+ @NonNull Class<NewArgumentsT> unused,
+ @NonNull Supplier<NewArgumentsBuilderT> argumentBuilderSupplier) {
return new ActionSpecBuilder<>(this.mCapabilityName, argumentBuilderSupplier);
}
@NonNull
public <NewOutputT>
- ActionSpecBuilder<PropertyT, ArgumentsT, ArgumentsBuilderT, NewOutputT> setOutput(
- @NonNull Class<NewOutputT> unused) {
+ ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, NewOutputT> setOutput(
+ @NonNull Class<NewOutputT> unused) {
return new ActionSpecBuilder<>(this.mCapabilityName, this.mArgumentBuilderSupplier);
}
/**
* Binds the parameter name, getter and setter.
*
- * @param paramName the name of this action' parameter.
- * @param paramGetter a getter of the param-specific info from the property.
+ * @param paramName the name of this action' parameter.
+ * @param paramGetter a getter of the param-specific info from the property.
* @param argumentSetter a setter to the argument with the input from {@code ParamValue}.
* @return the builder itself.
*/
@NonNull
- private ActionSpecBuilder<PropertyT, ArgumentsT, ArgumentsBuilderT, OutputT>
- bindParameterInternal(
- @NonNull String paramName,
- @NonNull Function<? super PropertyT, Optional<IntentParameter>> paramGetter,
- @NonNull ArgumentSetter<ArgumentsBuilderT> argumentSetter) {
+ private ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT>
+ bindParameterInternal(
+ @NonNull String paramName,
+ @NonNull Function<Map<String, Property<?>>,
+ Optional<IntentParameter>> paramGetter,
+ @NonNull ArgumentSetter<ArgumentsBuilderT> argumentSetter) {
mParamBindingList.add(ParamBinding.create(paramName, paramGetter, argumentSetter));
return this;
}
@@ -129,14 +119,15 @@
*/
@NonNull
public <T, PossibleValueT>
- ActionSpecBuilder<PropertyT, ArgumentsT, ArgumentsBuilderT, OutputT> bindParameter(
- @NonNull String paramName,
- @NonNull
- Function<? super PropertyT, Property<PossibleValueT>>
- propertyGetter,
- @NonNull BiConsumer<? super ArgumentsBuilderT, T> paramConsumer,
- @NonNull ParamValueConverter<T> paramValueConverter,
- @NonNull EntityConverter<PossibleValueT> entityConverter) {
+ ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT> bindParameter(
+ @NonNull String paramName,
+ @NonNull
+ Function<Map<String, Property<?>>,
+ Property<PossibleValueT>>
+ propertyGetter,
+ @NonNull BiConsumer<? super ArgumentsBuilderT, T> paramConsumer,
+ @NonNull ParamValueConverter<T> paramValueConverter,
+ @NonNull EntityConverter<PossibleValueT> entityConverter) {
return bindOptionalParameter(
paramName,
property -> Optional.of(propertyGetter.apply(property)),
@@ -163,17 +154,17 @@
*/
@NonNull
public <T, PossibleValueT>
- ActionSpecBuilder<PropertyT, ArgumentsT, ArgumentsBuilderT, OutputT>
- bindOptionalParameter(
- @NonNull String paramName,
- @NonNull
- Function<
- ? super PropertyT,
- Optional<Property<PossibleValueT>>>
- optionalPropertyGetter,
- @NonNull BiConsumer<? super ArgumentsBuilderT, T> paramConsumer,
- @NonNull ParamValueConverter<T> paramValueConverter,
- @NonNull EntityConverter<PossibleValueT> entityConverter) {
+ ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT>
+ bindOptionalParameter(
+ @NonNull String paramName,
+ @NonNull
+ Function<
+ Map<String, Property<?>>,
+ Optional<Property<PossibleValueT>>>
+ optionalPropertyGetter,
+ @NonNull BiConsumer<? super ArgumentsBuilderT, T> paramConsumer,
+ @NonNull ParamValueConverter<T> paramValueConverter,
+ @NonNull EntityConverter<PossibleValueT> entityConverter) {
return bindParameterInternal(
paramName,
property ->
@@ -200,17 +191,17 @@
*/
@NonNull
public <T, PossibleValueT>
- ActionSpecBuilder<PropertyT, ArgumentsT, ArgumentsBuilderT, OutputT>
- bindRepeatedParameter(
- @NonNull String paramName,
- @NonNull
- Function<
- ? super PropertyT,
- Optional<Property<PossibleValueT>>>
- optionalPropertyGetter,
- @NonNull BiConsumer<? super ArgumentsBuilderT, List<T>> paramConsumer,
- @NonNull ParamValueConverter<T> paramValueConverter,
- @NonNull EntityConverter<PossibleValueT> entityConverter) {
+ ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT>
+ bindRepeatedParameter(
+ @NonNull String paramName,
+ @NonNull
+ Function<
+ Map<String, Property<?>>,
+ Optional<Property<PossibleValueT>>>
+ optionalPropertyGetter,
+ @NonNull BiConsumer<? super ArgumentsBuilderT, List<T>> paramConsumer,
+ @NonNull ParamValueConverter<T> paramValueConverter,
+ @NonNull EntityConverter<PossibleValueT> entityConverter) {
return bindParameterInternal(
paramName,
property ->
@@ -227,17 +218,17 @@
/**
* Binds an optional output.
*
- * @param name the BII output slot name of this parameter.
+ * @param name the BII output slot name of this parameter.
* @param outputGetter a getter of the output from the {@code OutputT} instance.
- * @param converter a converter from an output object to a ParamValue.
+ * @param converter a converter from an output object to a ParamValue.
*/
@NonNull
@SuppressWarnings("JdkCollectors")
public <T>
- ActionSpecBuilder<PropertyT, ArgumentsT, ArgumentsBuilderT, OutputT> bindOptionalOutput(
- @NonNull String name,
- @NonNull Function<OutputT, Optional<T>> outputGetter,
- @NonNull Function<T, ParamValue> converter) {
+ ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT> bindOptionalOutput(
+ @NonNull String name,
+ @NonNull Function<OutputT, Optional<T>> outputGetter,
+ @NonNull Function<T, ParamValue> converter) {
mOutputBindings.put(
name,
output -> {
@@ -254,17 +245,17 @@
/**
* Binds a repeated output.
*
- * @param name the BII output slot name of this parameter.
+ * @param name the BII output slot name of this parameter.
* @param outputGetter a getter of the output from the {@code OutputT} instance.
- * @param converter a converter from an output object to a ParamValue.
+ * @param converter a converter from an output object to a ParamValue.
*/
@NonNull
@SuppressWarnings("JdkCollectors")
public <T>
- ActionSpecBuilder<PropertyT, ArgumentsT, ArgumentsBuilderT, OutputT> bindRepeatedOutput(
- @NonNull String name,
- @NonNull Function<OutputT, List<T>> outputGetter,
- @NonNull Function<T, ParamValue> converter) {
+ ActionSpecBuilder<ArgumentsT, ArgumentsBuilderT, OutputT> bindRepeatedOutput(
+ @NonNull String name,
+ @NonNull Function<OutputT, List<T>> outputGetter,
+ @NonNull Function<T, ParamValue> converter) {
mOutputBindings.put(
name,
output ->
@@ -276,7 +267,7 @@
/** Builds an {@code ActionSpec} from this builder. */
@NonNull
- public ActionSpec<PropertyT, ArgumentsT, OutputT> build() {
+ public ActionSpec<ArgumentsT, OutputT> build() {
return new ActionSpecImpl<>(
mCapabilityName,
mArgumentBuilderSupplier,
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecImpl.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecImpl.java
index 47f0a1a..8dfdb19 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecImpl.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecImpl.java
@@ -21,6 +21,7 @@
import androidx.annotation.NonNull;
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException;
+import androidx.appactions.interaction.capabilities.core.properties.Property;
import androidx.appactions.interaction.proto.AppActionsContext.AppAction;
import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput;
import androidx.appactions.interaction.proto.ParamValue;
@@ -33,18 +34,18 @@
/** The implementation of {@code ActionSpec} interface. */
final class ActionSpecImpl<
- PropertyT, ArgumentsT, ArgumentsBuilderT extends BuilderOf<ArgumentsT>, OutputT>
- implements ActionSpec<PropertyT, ArgumentsT, OutputT> {
+ ArgumentsT, ArgumentsBuilderT extends BuilderOf<ArgumentsT>, OutputT>
+ implements ActionSpec<ArgumentsT, OutputT> {
private final String mCapabilityName;
private final Supplier<ArgumentsBuilderT> mArgumentBuilderSupplier;
- private final List<ParamBinding<PropertyT, ArgumentsT, ArgumentsBuilderT>> mParamBindingList;
+ private final List<ParamBinding<ArgumentsT, ArgumentsBuilderT>> mParamBindingList;
private final Map<String, Function<OutputT, List<ParamValue>>> mOutputBindings;
ActionSpecImpl(
String capabilityName,
Supplier<ArgumentsBuilderT> argumentBuilderSupplier,
- List<ParamBinding<PropertyT, ArgumentsT, ArgumentsBuilderT>> paramBindingList,
+ List<ParamBinding<ArgumentsT, ArgumentsBuilderT>> paramBindingList,
Map<String, Function<OutputT, List<ParamValue>>> outputBindings) {
this.mCapabilityName = capabilityName;
this.mArgumentBuilderSupplier = argumentBuilderSupplier;
@@ -54,7 +55,7 @@
@NonNull
@Override
- public AppAction convertPropertyToProto(PropertyT property) {
+ public AppAction convertPropertyToProto(@NonNull Map<String, Property<?>> property) {
return AppAction.newBuilder()
.setName(mCapabilityName)
.addAllParams(
@@ -71,7 +72,7 @@
public ArgumentsT buildArguments(@NonNull Map<String, List<ParamValue>> args)
throws StructConversionException {
ArgumentsBuilderT argumentBuilder = mArgumentBuilderSupplier.get();
- for (ParamBinding<PropertyT, ArgumentsT, ArgumentsBuilderT> binding : mParamBindingList) {
+ for (ParamBinding<ArgumentsT, ArgumentsBuilderT> binding : mParamBindingList) {
List<ParamValue> paramValues = args.get(binding.name());
if (paramValues == null) {
continue;
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ParamBinding.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ParamBinding.java
index f21f6f2..87ce323 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ParamBinding.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ParamBinding.java
@@ -19,31 +19,30 @@
import androidx.annotation.NonNull;
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException;
+import androidx.appactions.interaction.capabilities.core.properties.Property;
import androidx.appactions.interaction.proto.AppActionsContext.IntentParameter;
import androidx.appactions.interaction.proto.ParamValue;
import com.google.auto.value.AutoValue;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
/**
* A binding between a parameter and its Property converter / Argument setter.
- *
- * @param <PropertyT>
- * @param <ArgumentsT>
- * @param <ArgumentsBuilderT>
*/
@AutoValue
public abstract class ParamBinding<
- PropertyT, ArgumentsT, ArgumentsBuilderT extends BuilderOf<ArgumentsT>> {
+ ArgumentsT, ArgumentsBuilderT extends BuilderOf<ArgumentsT>> {
- static <PropertyT, ArgumentsT, ArgumentsBuilderT extends BuilderOf<ArgumentsT>>
- ParamBinding<PropertyT, ArgumentsT, ArgumentsBuilderT> create(
- String name,
- Function<? super PropertyT, Optional<IntentParameter>> paramGetter,
- ArgumentSetter<ArgumentsBuilderT> argumentSetter) {
+ static <ArgumentsT, ArgumentsBuilderT extends BuilderOf<ArgumentsT>>
+ ParamBinding<ArgumentsT, ArgumentsBuilderT> create(
+ String name,
+ Function<Map<String, Property<?>>,
+ Optional<IntentParameter>> paramGetter,
+ ArgumentSetter<ArgumentsBuilderT> argumentSetter) {
return new AutoValue_ParamBinding<>(name, paramGetter, argumentSetter);
}
@@ -52,11 +51,13 @@
public abstract String name();
/**
- * Converts a {@code PropertyT} to an {@code IntentParameter} proto. The resulting proto is the
+ * Converts a {@code Property Map} to an {@code IntentParameter} proto. The resulting proto is
+ * the
* format which we send the current params to Assistant (via. app actions context).
*/
@NonNull
- public abstract Function<? super PropertyT, Optional<IntentParameter>> paramGetter();
+ public abstract Function<Map<String, Property<?>>,
+ Optional<IntentParameter>> paramGetter();
/**
* Populates the {@code ArgumentsBuilderT} for this param with the {@code ParamValue} sent from
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilityImpl.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilityImpl.kt
index 3f90e23..fc9eeaf 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilityImpl.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilityImpl.kt
@@ -21,6 +21,7 @@
import androidx.appactions.interaction.capabilities.core.HostProperties
import androidx.appactions.interaction.capabilities.core.impl.CapabilitySession
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
+import androidx.appactions.interaction.capabilities.core.properties.Property
import androidx.appactions.interaction.proto.AppActionsContext.AppAction
import androidx.appactions.interaction.proto.TaskInfo
import java.util.function.Supplier
@@ -34,7 +35,6 @@
* @param sessionUpdaterSupplier a Supplier of SessionUpdaterT instances
*/
internal class TaskCapabilityImpl<
- PropertyT,
ArgumentsT,
OutputT,
ExecutionSessionT : BaseExecutionSession<ArgumentsT, OutputT>,
@@ -43,8 +43,8 @@
>
constructor(
id: String,
- private val actionSpec: ActionSpec<PropertyT, ArgumentsT, OutputT>,
- private val property: PropertyT,
+ private val actionSpec: ActionSpec<ArgumentsT, OutputT>,
+ private val property: Map<String, Property<*>>,
private val sessionFactory: (hostProperties: HostProperties?) -> ExecutionSessionT?,
private val sessionBridge: SessionBridge<ExecutionSessionT, ConfirmationT>,
private val sessionUpdaterSupplier: Supplier<SessionUpdaterT>
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilitySession.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilitySession.kt
index 168ab619..1f577f56 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilitySession.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilitySession.kt
@@ -39,7 +39,7 @@
ConfirmationT,
>(
override val sessionId: String,
- actionSpec: ActionSpec<*, ArgumentsT, OutputT>,
+ actionSpec: ActionSpec<ArgumentsT, OutputT>,
appAction: AppAction,
taskHandler: TaskHandler<ConfirmationT>,
externalSession: BaseExecutionSession<ArgumentsT, OutputT>,
@@ -51,7 +51,6 @@
// single-turn capability does not have status
override val isActive: Boolean
get() = when (sessionOrchestrator.status) {
- TaskOrchestrator.Status.COMPLETED,
TaskOrchestrator.Status.DESTROYED -> false
else -> true
}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskOrchestrator.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskOrchestrator.kt
index 5e3aaf5..5b51ac3 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskOrchestrator.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskOrchestrator.kt
@@ -58,7 +58,7 @@
*/
internal class TaskOrchestrator<ArgumentsT, OutputT, ConfirmationT>(
private val sessionId: String,
- private val actionSpec: ActionSpec<*, ArgumentsT, OutputT>,
+ private val actionSpec: ActionSpec<ArgumentsT, OutputT>,
private val appAction: AppActionsContext.AppAction,
private val taskHandler: TaskHandler<ConfirmationT>,
private val externalSession: BaseExecutionSession<ArgumentsT, OutputT>,
@@ -68,7 +68,6 @@
internal enum class Status {
UNINITIATED,
IN_PROGRESS,
- COMPLETED,
DESTROYED,
}
/**
@@ -147,7 +146,14 @@
inProgress = true
}
try {
- if (updateRequest.assistantRequest != null) {
+ if (status == Status.DESTROYED) {
+ if (updateRequest.assistantRequest != null) {
+ FulfillmentResult(ErrorStatusInternal.SESSION_ALREADY_DESTROYED)
+ .applyToCallback(updateRequest.assistantRequest.callbackInternal)
+ } else if (updateRequest.touchEventRequest != null && touchEventCallback != null) {
+ touchEventCallback!!.onError(ErrorStatusInternal.SESSION_ALREADY_DESTROYED)
+ }
+ } else if (updateRequest.assistantRequest != null) {
processAssistantUpdateRequest(updateRequest.assistantRequest)
} else if (updateRequest.touchEventRequest != null) {
processTouchEventUpdateRequest(updateRequest.touchEventRequest)
@@ -177,22 +183,22 @@
val callback = assistantUpdateRequest.callbackInternal
val fulfillmentResult: FulfillmentResult
if (argumentsWrapper.requestMetadata == null) {
- fulfillmentResult = FulfillmentResult(ErrorStatusInternal.INVALID_REQUEST_TYPE)
+ fulfillmentResult = FulfillmentResult(ErrorStatusInternal.INVALID_REQUEST)
} else {
fulfillmentResult = when (argumentsWrapper.requestMetadata.requestType()) {
FulfillmentRequest.Fulfillment.Type.UNRECOGNIZED,
FulfillmentRequest.Fulfillment.Type.UNKNOWN_TYPE,
->
- FulfillmentResult(ErrorStatusInternal.INVALID_REQUEST_TYPE)
+ FulfillmentResult(ErrorStatusInternal.INVALID_REQUEST)
FulfillmentRequest.Fulfillment.Type.SYNC -> handleSync(argumentsWrapper)
FulfillmentRequest.Fulfillment.Type.CONFIRM -> handleConfirm()
- FulfillmentRequest.Fulfillment.Type.CANCEL,
- FulfillmentRequest.Fulfillment.Type.TERMINATE,
+ FulfillmentRequest.Fulfillment.Type.CANCEL
-> {
terminate()
FulfillmentResult(FulfillmentResponse.getDefaultInstance())
}
+ else -> FulfillmentResult(ErrorStatusInternal.INVALID_REQUEST)
}
}
fulfillmentResult.applyToCallback(callback)
@@ -254,7 +260,6 @@
}
}
- // TODO: add cleanup logic if any
internal fun terminate() {
externalSession.onDestroy()
status = Status.DESTROYED
@@ -471,7 +476,7 @@
val result = invokeExternalSuspendBlock("onExecute") {
externalSession.onExecute(actionSpec.buildArguments(finalArguments))
}
- status = Status.COMPLETED
+ terminate()
val fulfillmentResponse =
FulfillmentResponse.newBuilder().setStartDictation(result.shouldStartDictation)
convertToExecutionOutput(result)?.let { fulfillmentResponse.executionOutput = it }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/utils/CallbackUtils.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/utils/CallbackUtils.kt
index 8cc223d..1811abd 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/utils/CallbackUtils.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/utils/CallbackUtils.kt
@@ -16,11 +16,15 @@
package androidx.appactions.interaction.capabilities.core.impl.utils
+import androidx.appactions.interaction.capabilities.core.impl.ErrorStatusInternal
import androidx.appactions.interaction.capabilities.core.impl.exceptions.ExternalException
+import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException
+import androidx.appactions.interaction.capabilities.core.impl.exceptions.InvalidRequestException
+import kotlin.reflect.KClass
/** invoke an externally implemented method, wrapping any exceptions with ExternalException.
*/
-fun <T> invokeExternalBlock(description: String, block: () -> T): T {
+internal fun <T> invokeExternalBlock(description: String, block: () -> T): T {
try {
return block()
} catch (t: Throwable) {
@@ -31,10 +35,36 @@
/** invoke an externally implemented suspend method, wrapping any exceptions with
* ExternalException.
*/
-suspend fun <T> invokeExternalSuspendBlock(description: String, block: suspend () -> T): T {
+internal suspend fun <T> invokeExternalSuspendBlock(
+ description: String,
+ block: suspend () -> T
+): T {
try {
return block()
} catch (t: Throwable) {
throw ExternalException("exception occurred during '$description'", t)
}
+}
+
+/** Determines whether or not this exception is caused by some type, directly or indirectly. */
+internal fun <T : Throwable> Throwable.isCausedBy(clazz: KClass<T>): Boolean {
+ if (clazz.isInstance(this)) {
+ return true
+ }
+ return this.cause?.isCausedBy(clazz) == true
+}
+
+internal fun Throwable.toErrorStatusInternal(): ErrorStatusInternal {
+ return when {
+ this.isCausedBy(
+ ExternalException::class
+ ) -> ErrorStatusInternal.EXTERNAL_EXCEPTION
+ this.isCausedBy(
+ StructConversionException::class
+ ) -> ErrorStatusInternal.STRUCT_CONVERSION_FAILURE
+ this.isCausedBy(
+ InvalidRequestException::class
+ ) -> ErrorStatusInternal.INVALID_REQUEST
+ else -> ErrorStatusInternal.CANCELLED
+ }
}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityTest.kt
index 108565d..a1513d9 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityTest.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityTest.kt
@@ -19,7 +19,6 @@
import android.util.SizeF
import androidx.appactions.interaction.capabilities.core.ExecutionCallback
import androidx.appactions.interaction.capabilities.core.ExecutionCallbackAsync
-import androidx.appactions.interaction.capabilities.core.toExecutionCallback
import androidx.appactions.interaction.capabilities.core.ExecutionResult
import androidx.appactions.interaction.capabilities.core.HostProperties
import androidx.appactions.interaction.capabilities.core.impl.concurrent.Futures
@@ -28,13 +27,13 @@
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
import androidx.appactions.interaction.capabilities.core.properties.Property
import androidx.appactions.interaction.capabilities.core.properties.StringValue
-import androidx.appactions.interaction.capabilities.testing.internal.ArgumentUtils
-import androidx.appactions.interaction.capabilities.testing.internal.FakeCallbackInternal
-import androidx.appactions.interaction.capabilities.testing.internal.TestingUtils.CB_TIMEOUT
-import androidx.appactions.interaction.capabilities.testing.internal.TestingUtils.BLOCKING_TIMEOUT
import androidx.appactions.interaction.capabilities.core.testing.spec.Arguments
import androidx.appactions.interaction.capabilities.core.testing.spec.Output
-import androidx.appactions.interaction.capabilities.core.testing.spec.Properties
+import androidx.appactions.interaction.capabilities.core.toExecutionCallback
+import androidx.appactions.interaction.capabilities.testing.internal.ArgumentUtils
+import androidx.appactions.interaction.capabilities.testing.internal.FakeCallbackInternal
+import androidx.appactions.interaction.capabilities.testing.internal.TestingUtils.BLOCKING_TIMEOUT
+import androidx.appactions.interaction.capabilities.testing.internal.TestingUtils.CB_TIMEOUT
import androidx.appactions.interaction.proto.AppActionsContext.AppAction
import androidx.appactions.interaction.proto.AppActionsContext.IntentParameter
import androidx.appactions.interaction.proto.FulfillmentResponse
@@ -43,6 +42,7 @@
import androidx.appactions.interaction.proto.ParamValue
import androidx.appactions.interaction.proto.TaskInfo
import com.google.common.truth.Truth.assertThat
+import java.util.Optional
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeoutOrNull
@@ -51,6 +51,7 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
+@Suppress("UNCHECKED_CAST")
class SingleTurnCapabilityTest {
private val hostProperties =
HostProperties.Builder().setMaxHostSizeDp(SizeF(300f, 500f)).build()
@@ -62,13 +63,13 @@
val capability = SingleTurnCapabilityImpl(
id = "capabilityId",
actionSpec = ACTION_SPEC,
- property = Properties.newBuilder()
- .setRequiredStringField(
- Property.Builder<StringValue>().setPossibleValueSupplier(
+ property = mutableMapOf(
+ "requiredEntity" to Property
+ .Builder<StringValue>()
+ .setPossibleValueSupplier(
mutableEntityList::toList
).build()
- )
- .build(),
+ ),
executionCallback = ExecutionCallback<Arguments, Output> {
ExecutionResult.Builder<Output>().build()
}
@@ -126,17 +127,17 @@
)
.build()
}
+ val property = mutableMapOf<String, Property<*>>()
+ property.put(
+ "requiredString",
+ Property.Builder<StringValue>().build()
+ )
+ property.put("optionalString", Property.prohibited<StringValue>())
val capability =
SingleTurnCapabilityImpl(
id = "capabilityId",
actionSpec = ACTION_SPEC,
- property =
- Properties.newBuilder()
- .setRequiredStringField(
- Property.Builder<StringValue>().build()
- )
- .setOptionalStringField(Property.prohibited())
- .build(),
+ property = property,
executionCallback = executionCallback
)
@@ -181,20 +182,20 @@
}
@Test
- fun oneShotCapability_failure() {
+ fun oneShotCapability_exceptionInExecutionCallback() {
val executionCallback =
ExecutionCallback<Arguments, Output> { throw IllegalStateException("") }
+ val property = mutableMapOf<String, Property<*>>()
+ property.put(
+ "requiredString",
+ Property.Builder<StringValue>().build()
+ )
+ property.put("optionalString", Property.prohibited<StringValue>())
val capability =
SingleTurnCapabilityImpl(
id = "capabilityId",
actionSpec = ACTION_SPEC,
- property =
- Properties.newBuilder()
- .setRequiredStringField(
- Property.Builder<StringValue>().build()
- )
- .setOptionalStringField(Property.prohibited())
- .build(),
+ property = property,
executionCallback = executionCallback
)
@@ -212,23 +213,23 @@
val response = callbackInternal.receiveResponse()
assertThat(response.errorStatus).isNotNull()
- assertThat(response.errorStatus).isEqualTo(ErrorStatusInternal.CANCELLED)
+ assertThat(response.errorStatus).isEqualTo(ErrorStatusInternal.EXTERNAL_EXCEPTION)
}
@Test
fun oneShotSession_uiHandle_withExecutionCallback() {
val executionCallback =
ExecutionCallback<Arguments, Output> { ExecutionResult.Builder<Output>().build() }
+ val property = mutableMapOf<String, Property<*>>()
+ property.put(
+ "requiredString",
+ Property.Builder<StringValue>().build()
+ )
val capability =
SingleTurnCapabilityImpl(
id = "capabilityId",
actionSpec = ACTION_SPEC,
- property =
- Properties.newBuilder()
- .setRequiredStringField(
- Property.Builder<StringValue>().build()
- )
- .build(),
+ property = property,
executionCallback = executionCallback
)
val session = capability.createSession(fakeSessionId, hostProperties)
@@ -241,16 +242,16 @@
ExecutionCallbackAsync<Arguments, Output> {
Futures.immediateFuture(ExecutionResult.Builder<Output>().build())
}
+ val property = mutableMapOf<String, Property<*>>()
+ property.put(
+ "requiredString",
+ Property.Builder<StringValue>().build()
+ )
val capability =
SingleTurnCapabilityImpl(
id = "capabilityId",
actionSpec = ACTION_SPEC,
- property =
- Properties.newBuilder()
- .setRequiredStringField(
- Property.Builder<StringValue>().build()
- )
- .build(),
+ property = property,
executionCallback = executionCallbackAsync.toExecutionCallback()
)
val session = capability.createSession(fakeSessionId, hostProperties)
@@ -266,12 +267,15 @@
argumentChannel.send(it)
executionResultChannel.receive()
}
+ val property = mutableMapOf<String, Property<*>>()
+ property.put(
+ "requiredString",
+ Property.Builder<StringValue>().build()
+ )
val capability = SingleTurnCapabilityImpl(
id = "capabilityId",
actionSpec = ACTION_SPEC,
- property = Properties.newBuilder().setRequiredStringField(
- Property.Builder<StringValue>().build()
- ).build(),
+ property = property,
executionCallback = executionCallback
)
val session1 = capability.createSession("session1", hostProperties)
@@ -322,23 +326,27 @@
}
companion object {
- val ACTION_SPEC: ActionSpec<Properties, Arguments, Output> =
+ val ACTION_SPEC: ActionSpec<Arguments, Output> =
ActionSpecBuilder.ofCapabilityNamed(
"actions.intent.TEST"
)
- .setDescriptor(Properties::class.java)
.setArguments(Arguments::class.java, Arguments::newBuilder)
.setOutput(Output::class.java)
.bindParameter(
"requiredString",
- Properties::requiredStringField,
+ { properties -> properties["requiredEntity"] as Property<StringValue>? },
Arguments.Builder::setRequiredStringField,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER
)
.bindOptionalParameter(
"optionalString",
- Properties::optionalStringField,
+ { properties ->
+ properties["optionalString"]
+ ?.let { it as Property<StringValue> }
+ ?.let { Optional.of(it) }
+ ?: Optional.ofNullable(null)
+ },
Arguments.Builder::setOptionalStringField,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java
index 364bb86..6b7243a 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java
@@ -38,32 +38,46 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
-@RunWith(JUnit4.class)
-public final class ActionSpecTest {
- private static final ActionSpec<Properties, Arguments, Output> ACTION_SPEC =
+@RunWith(JUnit4.class)
+@SuppressWarnings("unchecked")
+public final class ActionSpecTest {
+ private static final ActionSpec<Arguments, Output> ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed("actions.intent.TEST")
- .setDescriptor(Properties.class)
.setArguments(Arguments.class, Arguments::newBuilder)
.setOutput(Output.class)
.bindParameter(
"requiredString",
- Properties::requiredStringField,
+ properties ->
+ {
+ return (Property<StringValue>) (properties.get(
+ "requiredString"));
+ },
Arguments.Builder::setRequiredStringField,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
.bindOptionalParameter(
"optionalString",
- Properties::optionalStringField,
+ properties ->
+ {
+ return Optional.ofNullable((Property<StringValue>) (properties.get(
+ "optionalString")));
+ },
Arguments.Builder::setOptionalStringField,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
.bindRepeatedParameter(
"repeatedString",
- Properties::repeatedStringField,
+ properties ->
+ {
+ return Optional.ofNullable((Property<StringValue>) (properties.get(
+ "repeatedString")));
+ },
Arguments.Builder::setRepeatedStringField,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
@@ -96,49 +110,61 @@
.setName(theString)
.build();
- private static final ActionSpec<GenericEntityProperty, GenericEntityArguments, Output>
+ private static final ActionSpec<GenericEntityArguments, Output>
GENERIC_TYPES_ACTION_SPEC =
- ActionSpecBuilder.ofCapabilityNamed("actions.intent.TEST")
- .setDescriptor(GenericEntityProperty.class)
- .setArguments(GenericEntityArguments.class,
- GenericEntityArguments::newBuilder)
- .setOutput(Output.class)
- .bindParameter(
- "requiredEntity",
- GenericEntityProperty::singularField,
- GenericEntityArguments.Builder::setSingularField,
- STRING_PARAM_VALUE_CONVERTER,
- STRING_ENTITY_CONVERTER)
- .bindOptionalParameter("optionalEntity",
- GenericEntityProperty::optionalField,
- GenericEntityArguments.Builder::setOptionalField,
- STRING_PARAM_VALUE_CONVERTER,
- STRING_ENTITY_CONVERTER)
- .bindRepeatedParameter("repeatedEntities",
- GenericEntityProperty::repeatedField,
- GenericEntityArguments.Builder::setRepeatedField,
- STRING_PARAM_VALUE_CONVERTER,
- STRING_ENTITY_CONVERTER)
- .build();
+ ActionSpecBuilder.ofCapabilityNamed("actions.intent.TEST")
+ .setArguments(GenericEntityArguments.class,
+ GenericEntityArguments::newBuilder)
+ .setOutput(Output.class)
+ .bindParameter(
+ "requiredEntity",
+ properties ->
+ {
+ return (Property<String>) (properties.get(
+ "requiredEntity"));
+ },
+ GenericEntityArguments.Builder::setSingularField,
+ STRING_PARAM_VALUE_CONVERTER,
+ STRING_ENTITY_CONVERTER)
+ .bindOptionalParameter("optionalEntity",
+ properties ->
+ {
+ return Optional.of((Property<String>) (properties.get(
+ "optionalEntity")));
+ },
+ GenericEntityArguments.Builder::setOptionalField,
+ STRING_PARAM_VALUE_CONVERTER,
+ STRING_ENTITY_CONVERTER)
+ .bindRepeatedParameter("repeatedEntities",
+ properties ->
+ {
+ return Optional.of((Property<String>) (properties.get(
+ "repeatedEntities")));
+ },
+ GenericEntityArguments.Builder::setRepeatedField,
+ STRING_PARAM_VALUE_CONVERTER,
+ STRING_ENTITY_CONVERTER)
+ .build();
@Test
public void getAppAction_genericParameters() {
- GenericEntityProperty property =
- GenericEntityProperty.create(
- new Property.Builder<String>()
- .setRequired(true)
- .setPossibleValues("one")
- .build(),
- Optional.of(
- new Property.Builder<String>()
- .setRequired(true)
- .setPossibleValues("two")
- .build()),
- Optional.of(
- new Property.Builder<String>()
- .setRequired(true)
- .setPossibleValues("three")
- .build()));
+ Map<String, Property<?>> property = new HashMap<>();
+ property.put("requiredEntity",
+ new Property.Builder<String>()
+ .setRequired(true)
+ .setPossibleValues("one")
+ .build());
+ property.put("optionalEntity",
+ new Property.Builder<String>()
+ .setRequired(true)
+ .setPossibleValues("two")
+ .build()
+ );
+ property.put("repeatedEntities",
+ new Property.Builder<String>()
+ .setRequired(true)
+ .setPossibleValues("three")
+ .build());
assertThat(GENERIC_TYPES_ACTION_SPEC.convertPropertyToProto(property))
.isEqualTo(
@@ -176,12 +202,12 @@
@Test
public void getAppAction_onlyRequiredProperty() {
- Properties property =
- Properties.create(
- new Property.Builder<StringValue>()
- .setPossibleValues(StringValue.of("Donald"))
- .setValueMatchRequired(true)
- .build());
+ Map<String, Property<?>> property = new HashMap<>();
+ property.put("requiredString",
+ new Property.Builder<StringValue>()
+ .setPossibleValues(StringValue.of("Donald"))
+ .setValueMatchRequired(true)
+ .build());
assertThat(ACTION_SPEC.convertPropertyToProto(property))
.isEqualTo(
@@ -201,21 +227,16 @@
@Test
public void getAppAction_allProperties() {
- Properties property =
- Properties.create(
- Optional.of(
- new Property.Builder<TestEnum>()
- .setPossibleValues(TestEnum.VALUE_1)
- .setRequired(true)
- .build()),
- new Property.Builder<StringValue>().build(),
- Optional.of(
- new Property.Builder<StringValue>()
- .setPossibleValues(StringValue.of("value1"))
- .setValueMatchRequired(true)
- .setRequired(true)
- .build()),
- Optional.of(Property.prohibited()));
+ Map<String, Property<?>> property = new HashMap<>();
+ property.put("requiredString",
+ new Property.Builder<StringValue>().build());
+ property.put("optionalString",
+ new Property.Builder<StringValue>()
+ .setPossibleValues(StringValue.of("value1"))
+ .setValueMatchRequired(true)
+ .setRequired(true)
+ .build());
+ property.put("repeatedString", Property.prohibited());
assertThat(ACTION_SPEC.convertPropertyToProto(property))
.isEqualTo(
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityImplTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityImplTest.kt
index 899825b..18c1cab 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityImplTest.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityImplTest.kt
@@ -19,9 +19,11 @@
import androidx.appactions.builtintypes.experimental.types.ListItem
import androidx.appactions.interaction.capabilities.core.AppEntityListener
import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.interaction.capabilities.core.ConfirmationOutput
import androidx.appactions.interaction.capabilities.core.EntitySearchResult
import androidx.appactions.interaction.capabilities.core.ExecutionResult
import androidx.appactions.interaction.capabilities.core.HostProperties
+import androidx.appactions.interaction.capabilities.core.SearchAction
import androidx.appactions.interaction.capabilities.core.SessionConfig
import androidx.appactions.interaction.capabilities.core.ValidationResult
import androidx.appactions.interaction.capabilities.core.ValueListener
@@ -33,10 +35,11 @@
import androidx.appactions.interaction.capabilities.core.impl.converters.SearchActionConverter
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters.LIST_ITEM_TYPE_SPEC
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeSpec
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.StringValue
import androidx.appactions.interaction.capabilities.core.properties.Property
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
import androidx.appactions.interaction.capabilities.core.testing.spec.Arguments
import androidx.appactions.interaction.capabilities.core.testing.spec.CapabilityStructFill
import androidx.appactions.interaction.capabilities.core.testing.spec.CapabilityTwoStrings
@@ -44,9 +47,6 @@
import androidx.appactions.interaction.capabilities.core.testing.spec.ExecutionSession
import androidx.appactions.interaction.capabilities.core.testing.spec.Output
import androidx.appactions.interaction.capabilities.core.testing.spec.TestEnum
-import androidx.appactions.interaction.capabilities.core.testing.spec.Properties
-import androidx.appactions.interaction.capabilities.core.SearchAction
-import androidx.appactions.interaction.capabilities.core.impl.converters.TypeSpec
import androidx.appactions.interaction.capabilities.testing.internal.ArgumentUtils.buildRequestArgs
import androidx.appactions.interaction.capabilities.testing.internal.ArgumentUtils.buildSearchActionParamValue
import androidx.appactions.interaction.capabilities.testing.internal.FakeCallbackInternal
@@ -59,6 +59,7 @@
import androidx.appactions.interaction.proto.DisambiguationData
import androidx.appactions.interaction.proto.Entity
import androidx.appactions.interaction.proto.FulfillmentRequest.Fulfillment.Type.CANCEL
+import androidx.appactions.interaction.proto.FulfillmentRequest.Fulfillment.Type
import androidx.appactions.interaction.proto.FulfillmentRequest.Fulfillment.Type.SYNC
import androidx.appactions.interaction.proto.FulfillmentRequest.Fulfillment.Type.UNKNOWN_TYPE
import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput
@@ -69,16 +70,18 @@
import androidx.concurrent.futures.CallbackToFutureAdapter.Completer
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.ListenableFuture
+import java.util.Optional
+import java.util.concurrent.atomic.AtomicInteger
+import java.util.concurrent.atomic.AtomicReference
+import java.util.function.Supplier
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-import java.util.concurrent.atomic.AtomicInteger
-import java.util.concurrent.atomic.AtomicReference
-import java.util.function.Supplier
@RunWith(JUnit4::class)
+@Suppress("UNCHECKED_CAST")
class TaskCapabilityImplTest {
private val capability: Capability =
createCapability<EmptyTaskUpdater>(
@@ -122,13 +125,13 @@
fun appAction_computedProperty() {
val mutableEntityList = mutableListOf<StringValue>()
val capability = createCapability<EmptyTaskUpdater>(
- Properties.newBuilder()
- .setRequiredStringField(
- Property.Builder<StringValue>()
- .setPossibleValueSupplier(mutableEntityList::toList)
- .build()
- )
- .build(),
+ mutableMapOf(
+ "required" to Property
+ .Builder<StringValue>()
+ .setPossibleValueSupplier(
+ mutableEntityList::toList
+ ).build()
+ ),
sessionFactory =
{
object : ExecutionSession {
@@ -346,26 +349,21 @@
val callback = FakeCallbackInternal()
session.execute(buildRequestArgs(UNKNOWN_TYPE), callback)
assertThat(callback.receiveResponse().errorStatus)
- .isEqualTo(ErrorStatusInternal.INVALID_REQUEST_TYPE)
+ .isEqualTo(ErrorStatusInternal.INVALID_REQUEST)
}
@Test
fun slotFilling_isActive_smokeTest() {
- val property: CapabilityTwoStrings.Properties =
- CapabilityTwoStrings.Properties.newBuilder()
- .setStringSlotA(
- Property.Builder<StringValue>()
- .setRequired(true)
- .build()
- )
- .setStringSlotB(
- Property.Builder<StringValue>()
- .setRequired(true)
- .build()
- )
- .build()
+ val property = mapOf(
+ "stringSlotA" to Property.Builder<StringValue>()
+ .setRequired(true)
+ .build(),
+ "stringSlotB" to Property.Builder<StringValue>()
+ .setRequired(true)
+ .build(),
+ )
val sessionFactory:
- (hostProperties: HostProperties?) -> CapabilityTwoStrings.ExecutionSession =
+ (hostProperties: HostProperties?) -> CapabilityTwoStrings.ExecutionSession =
{ _ ->
object : CapabilityTwoStrings.ExecutionSession {
override suspend fun onExecute(
@@ -435,7 +433,6 @@
buildRequestArgs(CANCEL),
callback3
)
- assertThat(callback3.receiveResponse().fulfillmentResponse).isNotNull()
assertThat(session.isActive).isFalse()
}
@@ -443,25 +440,16 @@
@kotlin.Throws(Exception::class)
fun slotFilling_optionalButRejectedParam_onFinishNotInvoked() {
val onExecuteInvocationCount = AtomicInteger(0)
- val property: CapabilityTwoStrings.Properties =
- CapabilityTwoStrings.Properties.newBuilder()
- .setStringSlotA(
- Property.Builder<
- StringValue
- >()
- .setRequired(true)
- .build()
- )
- .setStringSlotB(
- Property.Builder<
- StringValue
- >()
- .setRequired(false)
- .build()
- )
- .build()
+ val property = mapOf(
+ "stringSlotA" to Property.Builder<StringValue>()
+ .setRequired(true)
+ .build(),
+ "stringSlotB" to Property.Builder<StringValue>()
+ .setRequired(false)
+ .build(),
+ )
val sessionFactory:
- (hostProperties: HostProperties?) -> CapabilityTwoStrings.ExecutionSession =
+ (hostProperties: HostProperties?) -> CapabilityTwoStrings.ExecutionSession =
{ _ ->
object : CapabilityTwoStrings.ExecutionSession {
override suspend fun onExecute(
@@ -535,22 +523,16 @@
@Test
@kotlin.Throws(Exception::class)
fun slotFilling_assistantRemovedParam_clearInSdkState() {
- val property: Properties =
- Properties.newBuilder()
- .setRequiredStringField(
- Property.Builder<
- StringValue
- >()
- .setRequired(true)
- .build()
- )
- .setEnumField(
- Property.Builder<TestEnum>()
- .setPossibleValues(TestEnum.VALUE_1, TestEnum.VALUE_2)
- .setRequired(true)
- .build()
- )
- .build()
+ val property = mapOf(
+ "required" to
+ Property.Builder<StringValue>()
+ .setRequired(true)
+ .build(),
+ "optionalEnum" to Property.Builder<TestEnum>()
+ .setPossibleValues(TestEnum.VALUE_1, TestEnum.VALUE_2)
+ .setRequired(true)
+ .build(),
+ )
val capability: Capability =
createCapability(
property,
@@ -736,11 +718,16 @@
@kotlin.Throws(Exception::class)
@Suppress("DEPRECATION") // TODO(b/269638788) migrate session state to AppDialogState message
fun identifierOnly_refillsStruct() = runBlocking<Unit> {
- val property: CapabilityStructFill.Properties =
- CapabilityStructFill.Properties.newBuilder()
- .setListItem(Property.Builder<ListItem>().setRequired(true).build())
- .setAnyString(Property.Builder<StringValue>().setRequired(true).build())
- .build()
+ val property = mapOf(
+ "listItem" to Property.Builder<
+ ListItem,
+ >()
+ .setRequired(true)
+ .build(),
+ "anyString" to Property.Builder<StringValue>()
+ .setRequired(true)
+ .build(),
+ )
val item1: ListItem = ListItem.Builder().setName("red apple").setIdentifier("item1").build()
val item2: ListItem =
ListItem.Builder().setName("green apple").setIdentifier("item2").build()
@@ -749,7 +736,7 @@
val onExecuteStringDeferred = CompletableDeferred<String>()
val sessionFactory:
- (hostProperties: HostProperties?) -> CapabilityStructFill.ExecutionSession =
+ (hostProperties: HostProperties?) -> CapabilityStructFill.ExecutionSession =
{ _ ->
object : CapabilityStructFill.ExecutionSession {
override suspend fun onExecute(
@@ -788,7 +775,7 @@
TaskHandler.Builder<Void>()
.registerAppEntityTaskParam(
"listItem",
- session.getListItemListener(),
+ session.listItemListener,
ParamValueConverter.of(LIST_ITEM_TYPE_SPEC),
EntityConverter.of(LIST_ITEM_TYPE_SPEC)::convert,
getTrivialSearchActionConverter()
@@ -938,10 +925,10 @@
assertThat(
callback.receiveResponse()
.fulfillmentResponse!!
- .getExecutionOutput()
- .getOutputValuesList()
+ .executionOutput
+ .outputValuesList
)
- .containsExactlyElementsIn(expectedOutput.getOutputValuesList())
+ .containsExactlyElementsIn(expectedOutput.outputValuesList)
}
@Test
@@ -973,6 +960,158 @@
assertThat(callback.receiveResponse().fulfillmentResponse!!.startDictation).isTrue()
}
+ @Test
+ @kotlin.Throws(Exception::class)
+ fun fulfillmentType_finalSync_stateCleared() {
+ val sessionFactory: (hostProperties: HostProperties?) -> ExecutionSession =
+ { _ ->
+ object : ExecutionSession {
+ override suspend fun onExecute(arguments: Arguments) =
+ ExecutionResult.Builder<Output>().build()
+ }
+ }
+ val property = mapOf(
+ "required" to Property.Builder<StringValue>().setRequired(true).build()
+ )
+ val capability: Capability =
+ createCapability(
+ property,
+ sessionFactory = sessionFactory,
+ sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
+ sessionUpdaterSupplier = ::EmptyTaskUpdater,
+ )
+ val session = capability.createSession(fakeSessionId, hostProperties)
+
+ // TURN 1. Not providing all the required slots in the SYNC Request
+ val callback = FakeCallbackInternal()
+ session.execute(
+ buildRequestArgs(SYNC),
+ callback,
+ )
+ assertThat(callback.receiveResponse()).isNotNull()
+ assertThat(getCurrentValues("required", session.state!!)).isEmpty()
+ assertThat(session.isActive).isEqualTo(true)
+
+ // TURN 2. Providing the required slots so that the task completes and the state gets cleared
+ val callback2 = FakeCallbackInternal()
+ session.execute(
+ buildRequestArgs(SYNC,
+ "required",
+ ParamValue.newBuilder().setIdentifier("foo").setStringValue("foo").build()
+ ),
+ callback2,
+ )
+ assertThat(callback2.receiveResponse().fulfillmentResponse).isNotNull()
+ assertThat(session.isActive).isEqualTo(false)
+ }
+
+ @Test
+ @kotlin.Throws(Exception::class)
+ @Suppress("DEPRECATION") // TODO(b/279830425) implement tryExecute (INTENT_CONFIRMED can be used instead)
+ fun fulfillmentType_syncWithConfirmation_stateClearedAfterConfirmation() {
+ val sessionFactory: (hostProperties: HostProperties?) -> ExecutionSession =
+ { _ ->
+ object : ExecutionSession {
+ override suspend fun onExecute(arguments: Arguments) =
+ ExecutionResult.Builder<Output>().build()
+ }
+ }
+ var onReadyToConfirm =
+ object : OnReadyToConfirmListenerInternal<Confirmation> {
+ override suspend fun onReadyToConfirm(args: Map<String, List<ParamValue>>):
+ ConfirmationOutput<Confirmation> {
+ return ConfirmationOutput.Builder<Confirmation>()
+ .setConfirmation(Confirmation.builder().setOptionalStringField("bar")
+ .build())
+ .build()
+ }
+ }
+ val property = mapOf(
+ "required" to Property.Builder<StringValue>().setRequired(true).build()
+ )
+ val capability: Capability =
+ createCapability(
+ property,
+ sessionFactory = sessionFactory,
+ sessionBridge = SessionBridge {
+ TaskHandler.Builder<Confirmation>()
+ .setOnReadyToConfirmListenerInternal(onReadyToConfirm)
+ .build() },
+ sessionUpdaterSupplier = ::EmptyTaskUpdater,
+ )
+ val session = capability.createSession(fakeSessionId, hostProperties)
+
+ // TURN 1. Providing all the required slots in the SYNC Request
+ val callback = FakeCallbackInternal()
+ session.execute(
+ buildRequestArgs(SYNC,
+ "required",
+ ParamValue.newBuilder().setIdentifier("foo").setStringValue("foo").build()
+ ),
+ callback,
+ )
+ assertThat(callback.receiveResponse()).isNotNull()
+ assertThat(session.isActive).isEqualTo(true)
+
+ // Sending the confirmation request. After the confirm request, the session should not be
+ // active
+ val callback2 = FakeCallbackInternal()
+ session.execute(
+ buildRequestArgs(Type.CONFIRM),
+ callback2
+ )
+
+ assertThat(callback2.receiveResponse().fulfillmentResponse).isNotNull()
+ assertThat(session.isActive).isEqualTo(false)
+ }
+
+ @Test
+ fun fulfillmentRequest_whenStatusDestroyed_errorReported() {
+ val sessionFactory: (hostProperties: HostProperties?) -> ExecutionSession =
+ { _ ->
+ object : ExecutionSession {
+ override suspend fun onExecute(arguments: Arguments) =
+ ExecutionResult.Builder<Output>().build()
+ }
+ }
+ val property = mapOf(
+ "required" to Property.Builder<StringValue>().setRequired(true).build()
+ )
+ val capability: Capability =
+ createCapability(
+ property,
+ sessionFactory = sessionFactory,
+ sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
+ sessionUpdaterSupplier = ::EmptyTaskUpdater,
+ )
+ val session = capability.createSession(fakeSessionId, hostProperties)
+
+ // TURN 1. Providing the required slots so that the task completes and the state gets cleared
+ val callback = FakeCallbackInternal()
+ session.execute(
+ buildRequestArgs(SYNC,
+ "required",
+ ParamValue.newBuilder().setIdentifier("foo").setStringValue("foo").build()
+ ),
+ callback,
+ )
+ assertThat(callback.receiveResponse().fulfillmentResponse).isNotNull()
+ assertThat(session.isActive).isEqualTo(false)
+
+ // TURN 2. Trying to sync after the session is destroyed
+ val callback2 = FakeCallbackInternal()
+ session.execute(
+ buildRequestArgs(SYNC,
+ "required",
+ ParamValue.newBuilder().setIdentifier("foo").setStringValue("foo").build()
+ ),
+ callback2,
+ )
+ assertThat(session.isActive).isEqualTo(false)
+ assertThat(callback2.receiveResponse().errorStatus)
+ .isEqualTo(ErrorStatusInternal.SESSION_ALREADY_DESTROYED)
+ }
+
/**
* an implementation of Capability.Builder using Argument. Output, etc. defined under
* testing/spec
@@ -980,7 +1119,6 @@
class CapabilityBuilder :
Capability.Builder<
CapabilityBuilder,
- Properties,
Arguments,
Output,
Confirmation,
@@ -994,10 +1132,6 @@
override val sessionBridge: SessionBridge<ExecutionSession, Confirmation> = SessionBridge {
TaskHandler.Builder<Confirmation>().build()
}
-
- public override fun setExecutionSessionFactory(
- sessionFactory: (hostProperties: HostProperties?) -> ExecutionSession,
- ): CapabilityBuilder = super.setExecutionSessionFactory(sessionFactory)
}
companion object {
@@ -1054,37 +1188,55 @@
return ParamValue.newBuilder().build()
}
}
- private val ACTION_SPEC: ActionSpec<Properties, Arguments, Output> =
+ private val ACTION_SPEC: ActionSpec<Arguments, Output> =
ActionSpecBuilder.ofCapabilityNamed(
CAPABILITY_NAME
)
- .setDescriptor(Properties::class.java)
.setArguments(Arguments::class.java, Arguments::newBuilder)
.setOutput(Output::class.java)
.bindParameter(
"required",
- Properties::requiredStringField,
+ { properties ->
+ properties["required"]
+ as
+ Property<StringValue>?
+ },
Arguments.Builder::setRequiredStringField,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER
)
.bindOptionalParameter(
"optional",
- Properties::optionalStringField,
+ { properties ->
+ properties["optional"]
+ ?.let { it as Property<StringValue> }
+ ?.let { Optional.of(it) }
+ ?: Optional.ofNullable(null)
+ },
Arguments.Builder::setOptionalStringField,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER
)
.bindOptionalParameter(
"optionalEnum",
- Properties::enumField,
+ { properties ->
+ properties["optionalEnum"]
+ ?.let { it as Property<TestEnum> }
+ ?.let { Optional.of(it) }
+ ?: Optional.ofNullable(null)
+ },
Arguments.Builder::setEnumField,
ENUM_CONVERTER,
{ Entity.newBuilder().setIdentifier(it.toString()).build() }
)
.bindRepeatedParameter(
"repeated",
- Properties::repeatedStringField,
+ { properties ->
+ properties["repeated"]
+ ?.let { it as Property<StringValue> }
+ ?.let { Optional.of(it) }
+ ?: Optional.ofNullable(null)
+ },
Arguments.Builder::setRepeatedStringField,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER
@@ -1101,26 +1253,23 @@
)
.build()
- private val SINGLE_REQUIRED_FIELD_PROPERTY: Properties =
- Properties.newBuilder()
- .setRequiredStringField(
- Property.Builder<StringValue>()
- .setRequired(true)
- .build()
- )
+ private val SINGLE_REQUIRED_FIELD_PROPERTY = mapOf(
+ "required" to Property.Builder<StringValue>()
+ .setRequired(true)
.build()
+ )
private fun getCurrentValues(
argName: String,
appDialogState: AppDialogState
): List<CurrentValue> {
return appDialogState
- .getParamsList()
+ .paramsList
.stream()
- .filter { dialogParam -> dialogParam.getName().equals(argName) }
+ .filter { dialogParam -> dialogParam.name.equals(argName) }
.findFirst()
.orElse(DialogParameter.getDefaultInstance())
- .getCurrentValueList()
+ .currentValueList
}
/**
@@ -1128,12 +1277,11 @@
* etc., defined under ../../testing/spec
*/
private fun <SessionUpdaterT : AbstractTaskUpdater> createCapability(
- property: Properties,
+ property: Map<String, Property<*>>,
sessionFactory: (hostProperties: HostProperties?) -> ExecutionSession,
sessionBridge: SessionBridge<ExecutionSession, Confirmation>,
sessionUpdaterSupplier: Supplier<SessionUpdaterT>
): TaskCapabilityImpl<
- Properties,
Arguments,
Output,
ExecutionSession,
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.java
index a2317c7..586145d 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.java
@@ -36,22 +36,30 @@
import java.util.Optional;
/** Used to test the filling behavior of structured entities (e.g. ListItem) */
+@SuppressWarnings("unchecked")
public final class CapabilityStructFill {
private static final String CAPABILITY_NAME = "actions.intent.TEST";
- public static final ActionSpec<Properties, Arguments, Void> ACTION_SPEC =
+ public static final ActionSpec<Arguments, Void> ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(Properties.class)
.setArguments(Arguments.class, Arguments::newBuilder)
.bindOptionalParameter(
"listItem",
- Properties::listItem,
+ properties ->
+ {
+ return Optional.ofNullable((Property<ListItem>) (properties.get(
+ "listItem")));
+ },
Arguments.Builder::setListItem,
ParamValueConverter.Companion.of(LIST_ITEM_TYPE_SPEC),
EntityConverter.Companion.of(LIST_ITEM_TYPE_SPEC)::convert)
.bindOptionalParameter(
"string",
- Properties::anyString,
+ properties ->
+ {
+ return Optional.ofNullable((Property<StringValue>) (properties.get(
+ "anyString")));
+ },
Arguments.Builder::setAnyString,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.java
index 9595d9c..76cf08d 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.java
@@ -29,22 +29,29 @@
import java.util.Optional;
+@SuppressWarnings("unchecked")
public final class CapabilityTwoStrings {
-
private static final String CAPABILITY_NAME = "actions.intent.TEST";
- public static final ActionSpec<Properties, Arguments, Void> ACTION_SPEC =
+ public static final ActionSpec<Arguments, Void> ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(Properties.class)
.setArguments(Arguments.class, Arguments::newBuilder)
.bindOptionalParameter(
"stringSlotA",
- Properties::stringSlotA,
+ properties -> {
+ return Optional.ofNullable(
+ (Property<StringValue>) properties.get("stringSlotA")
+ );
+ },
Arguments.Builder::setStringSlotA,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
.bindOptionalParameter(
"stringSlotB",
- Properties::stringSlotB,
+ properties -> {
+ return Optional.ofNullable(
+ (Property<StringValue>) properties.get("stringSlotB")
+ );
+ },
Arguments.Builder::setStringSlotB,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER)
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetExerciseObservation.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetExerciseObservation.kt
index 66f19fa..d530c21 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetExerciseObservation.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetExerciseObservation.kt
@@ -16,8 +16,8 @@
package androidx.appactions.interaction.capabilities.fitness.fitness
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.CapabilityFactory
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
@@ -30,24 +30,34 @@
private const val CAPABILITY_NAME = "actions.intent.START_EXERCISE"
// TODO(b/273602015): Update to use Name property from builtintype library.
+@Suppress("UNCHECKED_CAST")
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(GetExerciseObservation.Properties::class.java)
.setArguments(
GetExerciseObservation.Arguments::class.java,
GetExerciseObservation.Arguments::Builder
)
.setOutput(GetExerciseObservation.Output::class.java)
.bindOptionalParameter(
- "healthObservation.startTime",
- { property -> Optional.ofNullable(property.startTime) },
+ "exerciseObservation.startTime",
+ { properties ->
+ Optional.ofNullable(
+ properties[GetExerciseObservation.PropertyMapStrings.START_TIME.key]
+ as Property<LocalTime>
+ )
+ },
GetExerciseObservation.Arguments.Builder::setStartTime,
TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER,
TypeConverters.LOCAL_TIME_ENTITY_CONVERTER
)
.bindOptionalParameter(
- "healthObservation.endTime",
- { property -> Optional.ofNullable(property.endTime) },
+ "exerciseObservation.endTime",
+ { properties ->
+ Optional.ofNullable(
+ properties[GetExerciseObservation.PropertyMapStrings.END_TIME.key]
+ as Property<LocalTime>
+ )
+ },
GetExerciseObservation.Arguments.Builder::setEndTime,
TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER,
TypeConverters.LOCAL_TIME_ENTITY_CONVERTER
@@ -56,64 +66,26 @@
@CapabilityFactory(name = CAPABILITY_NAME)
class GetExerciseObservation private constructor() {
- class CapabilityBuilder :
- Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
- >(ACTION_SPEC) {
- private var propertyBuilder: Properties.Builder = Properties.Builder()
- fun setStartTimeProperty(startTime: Property<LocalTime>): CapabilityBuilder = apply {
- propertyBuilder.setEndTime(startTime)
- }
-
- fun setEndTimeProperty(endTime: Property<LocalTime>): CapabilityBuilder = apply {
- propertyBuilder.setEndTime(endTime)
- }
-
- override fun build(): Capability {
- // TODO(b/268369632): Clean this up after Property is removed
- super.setProperty(propertyBuilder.build())
- return super.build()
- }
+ internal enum class PropertyMapStrings(val key: String) {
+ START_TIME("exerciseObservation.startTime"),
+ END_TIME("exerciseObservation.endTime"),
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties internal constructor(
- val startTime: Property<LocalTime>?,
- val endTime: Property<LocalTime>?
- ) {
- override fun toString(): String {
- return "Property(startTime=$startTime, endTime=$endTime)"
- }
+ class CapabilityBuilder :
+ Capability.Builder<
+ CapabilityBuilder, Arguments, Output, Confirmation, ExecutionSession
+ >(ACTION_SPEC) {
+ private var properties = mutableMapOf<String, Property<*>>()
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass !== other?.javaClass) return false
+ fun setStartTime(startTime: Property<LocalTime>): CapabilityBuilder =
+ apply { properties[PropertyMapStrings.START_TIME.key] = startTime }
- other as Properties
+ fun setEndTime(endTime: Property<LocalTime>): CapabilityBuilder =
+ apply { properties[PropertyMapStrings.END_TIME.key] = endTime }
- if (startTime != other.startTime) return false
- if (endTime != other.endTime) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- var result = startTime.hashCode()
- result += 31 * endTime.hashCode()
- return result
- }
-
- class Builder {
- private var startTime: Property<LocalTime>? = null
- private var endTime: Property<LocalTime>? = null
-
- fun setStartTime(startTime: Property<LocalTime>): Builder =
- apply { this.startTime = startTime }
-
- fun setEndTime(endTime: Property<LocalTime>): Builder =
- apply { this.endTime = endTime }
-
- fun build(): Properties = Properties(startTime, endTime)
+ override fun build(): Capability {
+ super.setProperty(properties)
+ return super.build()
}
}
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetHealthObservation.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetHealthObservation.kt
index df9e344..c7482e1 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetHealthObservation.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetHealthObservation.kt
@@ -16,8 +16,8 @@
package androidx.appactions.interaction.capabilities.fitness.fitness
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.CapabilityFactory
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
@@ -30,24 +30,34 @@
private const val CAPABILITY_NAME = "actions.intent.START_EXERCISE"
// TODO(b/273602015): Update to use Name property from builtintype library.
+@Suppress("UNCHECKED_CAST")
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(GetHealthObservation.Properties::class.java)
.setArguments(
GetHealthObservation.Arguments::class.java,
GetHealthObservation.Arguments::Builder
)
.setOutput(GetHealthObservation.Output::class.java)
.bindOptionalParameter(
- "exerciseObservation.startTime",
- { property -> Optional.ofNullable(property.startTime) },
+ "healthObservation.startTime",
+ { properties ->
+ Optional.ofNullable(
+ properties[GetHealthObservation.PropertyMapStrings.START_TIME.key]
+ as Property<LocalTime>
+ )
+ },
GetHealthObservation.Arguments.Builder::setStartTime,
TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER,
TypeConverters.LOCAL_TIME_ENTITY_CONVERTER
)
.bindOptionalParameter(
- "exerciseObservation.endTime",
- { property -> Optional.ofNullable(property.endTime) },
+ "healthObservation.endTime",
+ { properties ->
+ Optional.ofNullable(
+ properties[GetHealthObservation.PropertyMapStrings.END_TIME.key]
+ as Property<LocalTime>
+ )
+ },
GetHealthObservation.Arguments.Builder::setEndTime,
TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER,
TypeConverters.LOCAL_TIME_ENTITY_CONVERTER
@@ -56,64 +66,30 @@
@CapabilityFactory(name = CAPABILITY_NAME)
class GetHealthObservation private constructor() {
- class CapabilityBuilder :
- Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
- >(ACTION_SPEC) {
- private var propertyBuilder: Properties.Builder = Properties.Builder()
- fun setStartTimeProperty(startTime: Property<LocalTime>): CapabilityBuilder = apply {
- propertyBuilder.setEndTime(startTime)
- }
-
- fun setEndTimeProperty(endTime: Property<LocalTime>): CapabilityBuilder = apply {
- propertyBuilder.setEndTime(endTime)
- }
-
- override fun build(): Capability {
- // TODO(b/268369632): Clean this up after Property is removed
- super.setProperty(propertyBuilder.build())
- return super.build()
- }
+ internal enum class PropertyMapStrings(val key: String) {
+ START_TIME("healthObservation.startTime"),
+ END_TIME("healthObservation.endTime"),
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties internal constructor(
- val startTime: Property<LocalTime>?,
- val endTime: Property<LocalTime>?
- ) {
- override fun toString(): String {
- return "Property(startTime=$startTime, endTime=$endTime)"
- }
+ class CapabilityBuilder :
+ Capability.Builder<
+ CapabilityBuilder,
+ Arguments,
+ Output,
+ Confirmation,
+ ExecutionSession
+ >(ACTION_SPEC) {
+ private var properties = mutableMapOf<String, Property<*>>()
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass !== other?.javaClass) return false
+ fun setStartTime(startTime: Property<LocalTime>): CapabilityBuilder =
+ apply { properties[PropertyMapStrings.START_TIME.key] = startTime }
- other as Properties
+ fun setEndTime(endTime: Property<LocalTime>): CapabilityBuilder =
+ apply { properties[PropertyMapStrings.END_TIME.key] = endTime }
- if (startTime != other.startTime) return false
- if (endTime != other.endTime) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- var result = startTime.hashCode()
- result += 31 * endTime.hashCode()
- return result
- }
-
- class Builder {
- private var startTime: Property<LocalTime>? = null
- private var endTime: Property<LocalTime>? = null
-
- fun setStartTime(startTime: Property<LocalTime>): Builder =
- apply { this.startTime = startTime }
-
- fun setEndTime(endTime: Property<LocalTime>): Builder =
- apply { this.endTime = endTime }
-
- fun build(): Properties = Properties(startTime, endTime)
+ override fun build(): Capability {
+ super.setProperty(properties)
+ return super.build()
}
}
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/PauseExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/PauseExercise.kt
index 332207f..f04ceee 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/PauseExercise.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/PauseExercise.kt
@@ -16,28 +16,33 @@
package androidx.appactions.interaction.capabilities.fitness.fitness
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.CapabilityFactory
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.StringValue
import androidx.appactions.interaction.capabilities.core.properties.Property
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
import java.util.Optional
/** PauseExercise.kt in interaction-capabilities-fitness */
private const val CAPABILITY_NAME = "actions.intent.PAUSE_EXERCISE"
// TODO(b/273602015): Update to use Name property from builtintype library.
+@Suppress("UNCHECKED_CAST")
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(PauseExercise.Properties::class.java)
.setArguments(PauseExercise.Arguments::class.java, PauseExercise.Arguments::Builder)
.setOutput(PauseExercise.Output::class.java)
.bindOptionalParameter(
"exercise.name",
- { property -> Optional.ofNullable(property.name) },
+ { properties ->
+ Optional.ofNullable(
+ properties[PauseExercise.PropertyMapStrings.NAME.key]
+ as Property<StringValue>
+ )
+ },
PauseExercise.Arguments.Builder::setName,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER
@@ -46,53 +51,25 @@
@CapabilityFactory(name = CAPABILITY_NAME)
class PauseExercise private constructor() {
- class CapabilityBuilder :
- Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
- >(ACTION_SPEC) {
- private var propertyBuilder: Properties.Builder = Properties.Builder()
- fun setNameProperty(name: Property<StringValue>): CapabilityBuilder =
- apply {
- propertyBuilder.setName(name)
- }
-
- override fun build(): Capability {
- // TODO(b/268369632): Clean this up after Property is removed
- super.setProperty(propertyBuilder.build())
- return super.build()
- }
+ internal enum class PropertyMapStrings(val key: String) {
+ NAME("exercise.name"),
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties internal constructor(
- val name: Property<StringValue>?,
- ) {
- override fun toString(): String {
- return "Property(name=$name)"
- }
+ class CapabilityBuilder :
+ Capability.Builder<
+ CapabilityBuilder,
+ Arguments,
+ Output,
+ Confirmation,
+ ExecutionSession
+ >(ACTION_SPEC) {
+ private var properties = mutableMapOf<String, Property<*>>()
+ fun setName(name: Property<StringValue>): CapabilityBuilder =
+ apply { properties[PropertyMapStrings.NAME.key] = name }
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass !== other?.javaClass) return false
-
- other as Properties
-
- if (name != other.name) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- return name.hashCode()
- }
-
- class Builder {
- private var name: Property<StringValue>? = null
-
- fun setName(name: Property<StringValue>): Builder =
- apply { this.name = name }
-
- fun build(): Properties = Properties(name)
+ override fun build(): Capability {
+ super.setProperty(properties)
+ return super.build()
}
}
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/ResumeExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/ResumeExercise.kt
index 491e804..1881a7c 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/ResumeExercise.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/ResumeExercise.kt
@@ -16,28 +16,33 @@
package androidx.appactions.interaction.capabilities.fitness.fitness
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.CapabilityFactory
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.StringValue
import androidx.appactions.interaction.capabilities.core.properties.Property
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
import java.util.Optional
/** ResumeExercise.kt in interaction-capabilities-fitness */
private const val CAPABILITY_NAME = "actions.intent.RESUME_EXERCISE"
// TODO(b/273602015): Update to use Name property from builtintype library.
+@Suppress("UNCHECKED_CAST")
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(ResumeExercise.Properties::class.java)
.setArguments(ResumeExercise.Arguments::class.java, ResumeExercise.Arguments::Builder)
.setOutput(ResumeExercise.Output::class.java)
.bindOptionalParameter(
"exercise.name",
- { property -> Optional.ofNullable(property.name) },
+ { properties ->
+ Optional.ofNullable(
+ properties[ResumeExercise.PropertyMapStrings.NAME.key]
+ as Property<StringValue>
+ )
+ },
ResumeExercise.Arguments.Builder::setName,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER
@@ -46,53 +51,25 @@
@CapabilityFactory(name = CAPABILITY_NAME)
class ResumeExercise private constructor() {
- class CapabilityBuilder :
- Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
- >(ACTION_SPEC) {
- private var propertyBuilder: Properties.Builder = Properties.Builder()
- fun setNameProperty(name: Property<StringValue>): CapabilityBuilder =
- apply {
- propertyBuilder.setName(name)
- }
-
- override fun build(): Capability {
- // TODO(b/268369632): Clean this up after Property is removed
- super.setProperty(propertyBuilder.build())
- return super.build()
- }
+ internal enum class PropertyMapStrings(val key: String) {
+ NAME("exercise.name"),
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties internal constructor(
- val name: Property<StringValue>?,
- ) {
- override fun toString(): String {
- return "Property(name=$name)"
- }
+ class CapabilityBuilder :
+ Capability.Builder<
+ CapabilityBuilder,
+ Arguments,
+ Output,
+ Confirmation,
+ ExecutionSession
+ >(ACTION_SPEC) {
+ private var properties = mutableMapOf<String, Property<*>>()
+ fun setName(name: Property<StringValue>): CapabilityBuilder =
+ apply { properties[PropertyMapStrings.NAME.key] = name }
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass !== other?.javaClass) return false
-
- other as Properties
-
- if (name != other.name) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- return name.hashCode()
- }
-
- class Builder {
- private var name: Property<StringValue>? = null
-
- fun setName(name: Property<StringValue>): Builder =
- apply { this.name = name }
-
- fun build(): Properties = Properties(name)
+ override fun build(): Capability {
+ super.setProperty(properties)
+ return super.build()
}
}
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StartExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StartExercise.kt
index 04524b4..0847d33 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StartExercise.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StartExercise.kt
@@ -16,14 +16,14 @@
package androidx.appactions.interaction.capabilities.fitness.fitness
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.CapabilityFactory
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.StringValue
import androidx.appactions.interaction.capabilities.core.properties.Property
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
import java.time.Duration
import java.util.Optional
@@ -31,21 +31,31 @@
private const val CAPABILITY_NAME = "actions.intent.START_EXERCISE"
// TODO(b/273602015): Update to use Name property from builtintype library.
+@Suppress("UNCHECKED_CAST")
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(StartExercise.Properties::class.java)
.setArguments(StartExercise.Arguments::class.java, StartExercise.Arguments::Builder)
.setOutput(StartExercise.Output::class.java)
.bindOptionalParameter(
"exercise.duration",
- { property -> Optional.ofNullable(property.duration) },
+ { properties ->
+ Optional.ofNullable(
+ properties[StartExercise.PropertyMapStrings.DURATION.key]
+ as Property<Duration>
+ )
+ },
StartExercise.Arguments.Builder::setDuration,
TypeConverters.DURATION_PARAM_VALUE_CONVERTER,
TypeConverters.DURATION_ENTITY_CONVERTER
)
.bindOptionalParameter(
"exercise.name",
- { property -> Optional.ofNullable(property.name) },
+ { properties ->
+ Optional.ofNullable(
+ properties[StartExercise.PropertyMapStrings.NAME.key]
+ as Property<StringValue>
+ )
+ },
StartExercise.Arguments.Builder::setName,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER
@@ -54,65 +64,30 @@
@CapabilityFactory(name = CAPABILITY_NAME)
class StartExercise private constructor() {
- class CapabilityBuilder :
- Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
- >(ACTION_SPEC) {
- fun setDurationProperty(duration: Property<Duration>): CapabilityBuilder =
- apply {
- Properties.Builder().setDuration(duration).build()
- }
-
- fun setNameProperty(name: Property<StringValue>): CapabilityBuilder =
- apply {
- Properties.Builder().setName(name).build()
- }
-
- override fun build(): Capability {
- // TODO(b/268369632): No-op remove empty property builder after Property od removed
- super.setProperty(Properties.Builder().build())
- return super.build()
- }
+ internal enum class PropertyMapStrings(val key: String) {
+ NAME("exercise.name"),
+ DURATION("exercise.duration"),
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties internal constructor(
- val duration: Property<Duration>?,
- val name: Property<StringValue>?
- ) {
- override fun toString(): String {
- return "Property(duration=$duration, name=$name)"
- }
+ class CapabilityBuilder :
+ Capability.Builder<
+ CapabilityBuilder,
+ Arguments,
+ Output,
+ Confirmation,
+ ExecutionSession
+ >(ACTION_SPEC) {
+ private var properties = mutableMapOf<String, Property<*>>()
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass !== other?.javaClass) return false
+ fun setName(name: Property<StringValue>): CapabilityBuilder =
+ apply { properties[PropertyMapStrings.NAME.key] = name }
- other as Properties
+ fun setDuration(duration: Property<Duration>): CapabilityBuilder =
+ apply { properties[PropertyMapStrings.DURATION.key] = duration }
- if (duration != other.duration) return false
- if (name != other.name) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- var result = duration.hashCode()
- result += 31 * name.hashCode()
- return result
- }
-
- class Builder {
- private var duration: Property<Duration>? = null
- private var name: Property<StringValue>? = null
-
- fun setDuration(duration: Property<Duration>): Builder =
- apply { this.duration = duration }
-
- fun setName(name: Property<StringValue>): Builder =
- apply { this.name = name }
-
- fun build(): Properties = Properties(duration, name)
+ override fun build(): Capability {
+ super.setProperty(properties)
+ return super.build()
}
}
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StopExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StopExercise.kt
index 09690c52..18efde0 100644
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StopExercise.kt
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StopExercise.kt
@@ -16,8 +16,8 @@
package androidx.appactions.interaction.capabilities.fitness.fitness
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.CapabilityFactory
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
@@ -30,14 +30,19 @@
private const val CAPABILITY_NAME = "actions.intent.PAUSE_EXERCISE"
// TODO(b/273602015): Update to use Name property from builtintype library.
+@Suppress("UNCHECKED_CAST")
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(StopExercise.Properties::class.java)
.setArguments(StopExercise.Arguments::class.java, StopExercise.Arguments::Builder)
.setOutput(StopExercise.Output::class.java)
.bindOptionalParameter(
"exercise.name",
- { property -> Optional.ofNullable(property.name) },
+ { properties ->
+ Optional.ofNullable(
+ properties[StopExercise.PropertyMapStrings.NAME.key]
+ as Property<StringValue>
+ )
+ },
StopExercise.Arguments.Builder::setName,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER
@@ -46,53 +51,26 @@
@CapabilityFactory(name = CAPABILITY_NAME)
class StopExercise private constructor() {
- class CapabilityBuilder :
- Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
- >(ACTION_SPEC) {
- private var propertyBuilder: Properties.Builder = Properties.Builder()
- fun setNameProperty(name: Property<StringValue>): CapabilityBuilder =
- apply {
- propertyBuilder.setName(name)
- }
-
- override fun build(): Capability {
- // TODO(b/268369632): Clean this up after Property is removed
- super.setProperty(propertyBuilder.build())
- return super.build()
- }
+ internal enum class PropertyMapStrings(val key: String) {
+ NAME("exercise.name"),
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties internal constructor(
- val name: Property<StringValue>?,
- ) {
- override fun toString(): String {
- return "Property(name=$name)"
- }
+ class CapabilityBuilder :
+ Capability.Builder<
+ CapabilityBuilder,
+ Arguments,
+ Output,
+ Confirmation,
+ ExecutionSession
+ >(ACTION_SPEC) {
+ private var properties = mutableMapOf<String, Property<*>>()
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass !== other?.javaClass) return false
+ fun setName(name: Property<StringValue>): CapabilityBuilder =
+ apply { properties[PropertyMapStrings.NAME.key] = name }
- other as Properties
-
- if (name != other.name) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- return name.hashCode()
- }
-
- class Builder {
- private var name: Property<StringValue>? = null
-
- fun setName(name: Property<StringValue>): Builder =
- apply { this.name = name }
-
- fun build(): Properties = Properties(name)
+ override fun build(): Capability {
+ super.setProperty(properties)
+ return super.build()
}
}
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt
index 14c0fed..528a497 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt
@@ -18,8 +18,8 @@
import androidx.appactions.builtintypes.experimental.types.GenericErrorStatus
import androidx.appactions.builtintypes.experimental.types.SuccessStatus
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
@@ -32,14 +32,19 @@
/** PauseTimer.kt in interaction-capabilities-productivity */
private const val CAPABILITY_NAME = "actions.intent.PAUSE_TIMER"
+@Suppress("UNCHECKED_CAST")
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(PauseTimer.Properties::class.java)
.setArguments(PauseTimer.Arguments::class.java, PauseTimer.Arguments::Builder)
.setOutput(PauseTimer.Output::class.java)
.bindRepeatedParameter(
"timer",
- { property -> Optional.ofNullable(property.timerList) },
+ { properties ->
+ Optional.ofNullable(
+ properties[PauseTimer.PropertyMapStrings.TIMER_LIST.key]
+ as Property<TimerValue>
+ )
+ },
PauseTimer.Arguments.Builder::setTimerList,
TimerValue.PARAM_VALUE_CONVERTER,
TimerValue.ENTITY_CONVERTER
@@ -53,57 +58,29 @@
// TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
class PauseTimer private constructor() {
+ internal enum class PropertyMapStrings(val key: String) {
+ TIMER_LIST("timer.timerList"),
+ }
class CapabilityBuilder :
Capability.Builder<
CapabilityBuilder,
- Properties,
Arguments,
Output,
Confirmation,
- ExecutionSession,
- >(ACTION_SPEC) {
+ ExecutionSession
+ >(ACTION_SPEC) {
+ private var properties = mutableMapOf<String, Property<*>>()
+
+ fun setTimerList(timerList: Property<TimerValue>): CapabilityBuilder =
+ apply { properties[PropertyMapStrings.TIMER_LIST.key] = timerList }
+
override fun build(): Capability {
- super.setProperty(Properties.Builder().build())
+ super.setProperty(properties)
return super.build()
}
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties
- internal constructor(
- val timerList: Property<TimerValue>?,
- ) {
- override fun toString(): String {
- return "Property(timerList=$timerList}"
- }
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as Properties
-
- if (timerList != other.timerList) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- return timerList.hashCode()
- }
-
- class Builder {
- private var timerList: Property<TimerValue>? = null
-
- fun setTimerList(timerList: Property<TimerValue>): Builder = apply {
- this.timerList = timerList
- }
-
- fun build(): Properties = Properties(timerList)
- }
- }
-
class Arguments
internal constructor(
val timerList: List<TimerValue>?,
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt
index d05acf9..94d203b 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt
@@ -18,8 +18,8 @@
import androidx.appactions.builtintypes.experimental.types.GenericErrorStatus
import androidx.appactions.builtintypes.experimental.types.SuccessStatus
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
@@ -32,14 +32,19 @@
/** ResetTimer.kt in interaction-capabilities-productivity */
private const val CAPABILITY_NAME = "actions.intent.RESET_TIMER"
+@Suppress("UNCHECKED_CAST")
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(ResetTimer.Properties::class.java)
.setArguments(ResetTimer.Arguments::class.java, ResetTimer.Arguments::Builder)
.setOutput(ResetTimer.Output::class.java)
.bindRepeatedParameter(
"timer",
- { property -> Optional.ofNullable(property.timerList) },
+ { properties ->
+ Optional.ofNullable(
+ properties[ResetTimer.PropertyMapStrings.TIMER_LIST.key]
+ as Property<TimerValue>
+ )
+ },
ResetTimer.Arguments.Builder::setTimerList,
TimerValue.PARAM_VALUE_CONVERTER,
TimerValue.ENTITY_CONVERTER
@@ -53,49 +58,29 @@
// TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
class ResetTimer private constructor() {
+ internal enum class PropertyMapStrings(val key: String) {
+ TIMER_LIST("timer.timerList"),
+ }
class CapabilityBuilder :
Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
- >(ACTION_SPEC) {
+ CapabilityBuilder,
+ Arguments,
+ Output,
+ Confirmation,
+ ExecutionSession
+ >(ACTION_SPEC) {
+ private var properties = mutableMapOf<String, Property<*>>()
+
+ fun setTimerList(timerList: Property<TimerValue>): CapabilityBuilder =
+ apply { properties[PropertyMapStrings.TIMER_LIST.key] = timerList }
+
override fun build(): Capability {
- super.setProperty(Properties.Builder().build())
+ super.setProperty(properties)
return super.build()
}
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties internal constructor(val timerList: Property<TimerValue>?) {
- override fun toString(): String {
- return "Property(timerList=$timerList}"
- }
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as Properties
-
- if (timerList != other.timerList) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- return timerList.hashCode()
- }
-
- class Builder {
- private var timerList: Property<TimerValue>? = null
-
- fun setTimerList(timerList: Property<TimerValue>): Builder = apply {
- this.timerList = timerList
- }
-
- fun build(): Properties = Properties(timerList)
- }
- }
-
class Arguments internal constructor(val timerList: List<TimerValue>?) {
override fun toString(): String {
return "Arguments(timerList=$timerList)"
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt
index accb730..d967e45 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt
@@ -18,8 +18,8 @@
import androidx.appactions.builtintypes.experimental.types.GenericErrorStatus
import androidx.appactions.builtintypes.experimental.types.SuccessStatus
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
@@ -32,14 +32,19 @@
/** ResumeTimer.kt in interaction-capabilities-productivity */
private const val CAPABILITY_NAME = "actions.intent.RESUME_TIMER"
+@Suppress("UNCHECKED_CAST")
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(ResumeTimer.Properties::class.java)
.setArguments(ResumeTimer.Arguments::class.java, ResumeTimer.Arguments::Builder)
.setOutput(ResumeTimer.Output::class.java)
.bindRepeatedParameter(
"timer",
- { property -> Optional.ofNullable(property.timerList) },
+ { properties ->
+ Optional.ofNullable(
+ properties[ResumeTimer.PropertyMapStrings.TIMER_LIST.key]
+ as Property<TimerValue>
+ )
+ },
ResumeTimer.Arguments.Builder::setTimerList,
TimerValue.PARAM_VALUE_CONVERTER,
TimerValue.ENTITY_CONVERTER
@@ -53,49 +58,29 @@
// TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
class ResumeTimer private constructor() {
+ internal enum class PropertyMapStrings(val key: String) {
+ TIMER_LIST("timer.timerList"),
+ }
class CapabilityBuilder :
Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
- >(ACTION_SPEC) {
+ CapabilityBuilder,
+ Arguments,
+ Output,
+ Confirmation,
+ ExecutionSession
+ >(ACTION_SPEC) {
+ private var properties = mutableMapOf<String, Property<*>>()
+
+ fun setTimerList(timerList: Property<TimerValue>): CapabilityBuilder =
+ apply { properties[PropertyMapStrings.TIMER_LIST.key] = timerList }
+
override fun build(): Capability {
- super.setProperty(Properties.Builder().build())
+ super.setProperty(properties)
return super.build()
}
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties internal constructor(val timerList: Property<TimerValue>?) {
- override fun toString(): String {
- return "Property(timerList=$timerList}"
- }
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as Properties
-
- if (timerList != other.timerList) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- return timerList.hashCode()
- }
-
- class Builder {
- private var timerList: Property<TimerValue>? = null
-
- fun setTimerList(timerList: Property<TimerValue>): Builder = apply {
- this.timerList = timerList
- }
-
- fun build(): Properties = Properties(timerList)
- }
- }
-
class Arguments internal constructor(val timerList: List<TimerValue>?) {
override fun toString(): String {
return "Arguments(timerList=$timerList)"
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt
index 25ca3cc..fcd211c 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt
@@ -18,17 +18,17 @@
import androidx.appactions.builtintypes.experimental.types.GenericErrorStatus
import androidx.appactions.builtintypes.experimental.types.SuccessStatus
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.HostProperties
import androidx.appactions.interaction.capabilities.core.ValueListener
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.StringValue
-import androidx.appactions.interaction.capabilities.core.properties.Property
import androidx.appactions.interaction.capabilities.core.impl.task.SessionBridge
import androidx.appactions.interaction.capabilities.core.impl.task.TaskHandler
+import androidx.appactions.interaction.capabilities.core.properties.Property
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
import androidx.appactions.interaction.proto.ParamValue
import androidx.appactions.interaction.protobuf.Struct
import androidx.appactions.interaction.protobuf.Value
@@ -38,28 +38,43 @@
/** StartTimer.kt in interaction-capabilities-productivity */
private const val CAPABILITY_NAME = "actions.intent.START_TIMER"
+@Suppress("UNCHECKED_CAST")
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(StartTimer.Properties::class.java)
.setArguments(StartTimer.Arguments::class.java, StartTimer.Arguments::Builder)
.setOutput(StartTimer.Output::class.java)
.bindOptionalParameter(
"timer.identifier",
- { property -> Optional.ofNullable(property.identifier) },
+ { properties ->
+ Optional.ofNullable(
+ properties[StartTimer.PropertyMapStrings.IDENTIFIER.key]
+ as Property<StringValue>
+ )
+ },
StartTimer.Arguments.Builder::setIdentifier,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER,
)
.bindOptionalParameter(
"timer.name",
- { property -> Optional.ofNullable(property.name) },
+ { properties ->
+ Optional.ofNullable(
+ properties[StartTimer.PropertyMapStrings.NAME.key]
+ as Property<StringValue>
+ )
+ },
StartTimer.Arguments.Builder::setName,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER,
)
.bindOptionalParameter(
"timer.duration",
- { property -> Optional.ofNullable(property.duration) },
+ { properties ->
+ Optional.ofNullable(
+ properties[StartTimer.PropertyMapStrings.DURATION.key]
+ as Property<Duration>
+ )
+ },
StartTimer.Arguments.Builder::setDuration,
TypeConverters.DURATION_PARAM_VALUE_CONVERTER,
TypeConverters.DURATION_ENTITY_CONVERTER,
@@ -93,20 +108,47 @@
// TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
class StartTimer private constructor() {
+ internal enum class PropertyMapStrings(val key: String) {
+ TIMER_LIST("timer.timerList"),
+ IDENTIFIER("timer.identifier"),
+ NAME("timer.name"),
+ DURATION("timer.duration"),
+ }
class CapabilityBuilder :
Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession,
+ CapabilityBuilder,
+ Arguments,
+ Output,
+ Confirmation,
+ ExecutionSession
>(ACTION_SPEC) {
+ private var properties = mutableMapOf<String, Property<*>>()
override val sessionBridge: SessionBridge<ExecutionSession, Confirmation> = SESSION_BRIDGE
- public override fun setExecutionSessionFactory(
+ override fun setExecutionSessionFactory(
sessionFactory: (hostProperties: HostProperties?) -> ExecutionSession,
): CapabilityBuilder = super.setExecutionSessionFactory(sessionFactory)
+ fun setTimerList(timerList: Property<TimerValue>): CapabilityBuilder = apply {
+ properties[PropertyMapStrings.TIMER_LIST.key] = timerList
+ }
+
+ fun setIdentifier(identifier: Property<StringValue>): CapabilityBuilder = apply {
+ properties[PropertyMapStrings.IDENTIFIER.key] = identifier
+ }
+
+ fun setName(name: Property<StringValue>): CapabilityBuilder = apply {
+ properties[PropertyMapStrings.NAME.key] = name
+ }
+
+ fun setDuration(duration: Property<Duration>): CapabilityBuilder = apply {
+ properties[PropertyMapStrings.DURATION.key] = duration
+ }
+
override fun build(): Capability {
- super.setProperty(Properties.Builder().build())
+ super.setProperty(properties)
return super.build()
}
}
@@ -118,56 +160,6 @@
get() = null
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties
- internal constructor(
- val identifier: Property<StringValue>?,
- val name: Property<StringValue>?,
- val duration: Property<Duration>?,
- ) {
- override fun toString(): String {
- return "Property(identifier=$identifier,name=$name,duration=$duration}"
- }
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as Properties
-
- if (identifier != other.identifier) return false
- if (name != other.name) return false
- if (duration != other.duration) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- var result = identifier.hashCode()
- result += 31 * name.hashCode()
- result += 31 * duration.hashCode()
- return result
- }
-
- class Builder {
- private var identifier: Property<StringValue>? = null
- private var name: Property<StringValue>? = null
- private var duration: Property<Duration>? = null
-
- fun setIdentifier(identifier: Property<StringValue>): Builder = apply {
- this.identifier = identifier
- }
-
- fun setName(name: Property<StringValue>): Builder = apply { this.name = name }
-
- fun setDuration(duration: Property<Duration>): Builder = apply {
- this.duration = duration
- }
-
- fun build(): Properties = Properties(identifier, name, duration)
- }
- }
-
class Arguments internal constructor(
val identifier: String?,
val name: String?,
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt
index fa589e7..93f5b75 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt
@@ -18,8 +18,8 @@
import androidx.appactions.builtintypes.experimental.types.GenericErrorStatus
import androidx.appactions.builtintypes.experimental.types.SuccessStatus
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
@@ -32,14 +32,19 @@
/** StopTimer.kt in interaction-capabilities-productivity */
private const val CAPABILITY_NAME = "actions.intent.STOP_TIMER"
+@Suppress("UNCHECKED_CAST")
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(StopTimer.Properties::class.java)
.setArguments(StopTimer.Arguments::class.java, StopTimer.Arguments::Builder)
.setOutput(StopTimer.Output::class.java)
.bindRepeatedParameter(
"timer",
- { property -> Optional.ofNullable(property.timerList) },
+ { properties ->
+ Optional.ofNullable(
+ properties[StopTimer.PropertyMapStrings.TIMER_LIST.key]
+ as Property<TimerValue>
+ )
+ },
StopTimer.Arguments.Builder::setTimerList,
TimerValue.PARAM_VALUE_CONVERTER,
TimerValue.ENTITY_CONVERTER
@@ -53,49 +58,29 @@
// TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
class StopTimer private constructor() {
+ internal enum class PropertyMapStrings(val key: String) {
+ TIMER_LIST("timer.timerList"),
+ }
class CapabilityBuilder :
Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
+ CapabilityBuilder,
+ Arguments,
+ Output,
+ Confirmation,
+ ExecutionSession
>(ACTION_SPEC) {
+ private var properties = mutableMapOf<String, Property<*>>()
+
+ fun setTimerList(timerList: Property<TimerValue>): CapabilityBuilder =
+ apply { properties[PropertyMapStrings.TIMER_LIST.key] = timerList }
+
override fun build(): Capability {
- super.setProperty(Properties.Builder().build())
+ super.setProperty(properties)
return super.build()
}
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties internal constructor(val timerList: Property<TimerValue>?) {
- override fun toString(): String {
- return "Property(timerList=$timerList}"
- }
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as Properties
-
- if (timerList != other.timerList) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- return timerList.hashCode()
- }
-
- class Builder {
- private var timerList: Property<TimerValue>? = null
-
- fun setTimerList(timerList: Property<TimerValue>): Builder = apply {
- this.timerList = timerList
- }
-
- fun build(): Properties = Properties(timerList)
- }
- }
-
class Arguments internal constructor(val timerList: List<TimerValue>?) {
override fun toString(): String {
return "Arguments(timerList=$timerList)"
diff --git a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartEmergencySharing.kt b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartEmergencySharing.kt
index a0829de..11e0682 100644
--- a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartEmergencySharing.kt
+++ b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartEmergencySharing.kt
@@ -17,13 +17,14 @@
package androidx.appactions.interaction.capabilities.safety
import androidx.appactions.builtintypes.experimental.types.GenericErrorStatus
-import androidx.appactions.builtintypes.experimental.types.SuccessStatus
import androidx.appactions.builtintypes.experimental.types.NoInternetConnection
-import androidx.appactions.interaction.capabilities.core.Capability
+import androidx.appactions.builtintypes.experimental.types.SuccessStatus
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
+import androidx.appactions.interaction.capabilities.core.properties.Property
import androidx.appactions.interaction.capabilities.safety.executionstatus.EmergencySharingInProgress
import androidx.appactions.interaction.capabilities.safety.executionstatus.SafetyAccountNotLoggedIn
import androidx.appactions.interaction.capabilities.safety.executionstatus.SafetyFeatureNotOnboarded
@@ -37,7 +38,6 @@
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(StartEmergencySharing.Properties::class.java)
.setArguments(
StartEmergencySharing.Arguments::class.java,
StartEmergencySharing.Arguments::Builder
@@ -55,17 +55,16 @@
// TODO(b/267805819): Update to include the SessionFactory once Session API is ready.
class CapabilityBuilder :
Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession,
+ CapabilityBuilder, Arguments, Output, Confirmation, ExecutionSession,
>(ACTION_SPEC) {
+
+ private var properties = mutableMapOf<String, Property<*>>()
override fun build(): Capability {
- super.setProperty(Properties())
+ super.setProperty(properties)
return super.build()
}
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties internal constructor()
-
class Arguments internal constructor() {
class Builder : BuilderOf<Arguments> {
override fun build(): Arguments = Arguments()
diff --git a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt
index cd5b319..9bb6f01 100644
--- a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt
+++ b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt
@@ -21,8 +21,8 @@
import androidx.appactions.builtintypes.experimental.types.NoInternetConnection
import androidx.appactions.builtintypes.experimental.types.SafetyCheck
import androidx.appactions.builtintypes.experimental.types.SuccessStatus
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
@@ -42,21 +42,31 @@
/** StartSafetyCheck.kt in interaction-capabilities-safety */
private const val CAPABILITY_NAME = "actions.intent.START_SAFETY_CHECK"
+@Suppress("UNCHECKED_CAST")
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(StartSafetyCheck.Properties::class.java)
.setArguments(StartSafetyCheck.Arguments::class.java, StartSafetyCheck.Arguments::Builder)
.setOutput(StartSafetyCheck.Output::class.java)
.bindOptionalParameter(
"safetyCheck.duration",
- { property -> Optional.ofNullable(property.duration) },
+ { properties ->
+ Optional.ofNullable(
+ properties[StartSafetyCheck.PropertyMapStrings.DURATION.key]
+ as Property<Duration>
+ )
+ },
StartSafetyCheck.Arguments.Builder::setDuration,
TypeConverters.DURATION_PARAM_VALUE_CONVERTER,
TypeConverters.DURATION_ENTITY_CONVERTER
)
.bindOptionalParameter(
"safetyCheck.checkInTime",
- { property -> Optional.ofNullable(property.checkInTime) },
+ { property ->
+ Optional.ofNullable(
+ property[StartSafetyCheck.PropertyMapStrings.CHECK_IN_TIME.key]
+ as Property<ZonedDateTime>
+ )
+ },
StartSafetyCheck.Arguments.Builder::setCheckInTime,
TypeConverters.ZONED_DATETIME_PARAM_VALUE_CONVERTER,
TypeConverters.ZONED_DATETIME_ENTITY_CONVERTER
@@ -75,60 +85,30 @@
// TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
class StartSafetyCheck private constructor() {
+ internal enum class PropertyMapStrings(val key: String) {
+ DURATION("safetycheck.duration"),
+ CHECK_IN_TIME("safetycheck.checkInTime"),
+ }
+
// TODO(b/267805819): Update to include the SessionFactory once Session API is ready.
class CapabilityBuilder :
Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
+ CapabilityBuilder, Arguments, Output, Confirmation, ExecutionSession
>(ACTION_SPEC) {
+ private var properties = mutableMapOf<String, Property<*>>()
+
+ fun setDuration(duration: Property<Duration>): CapabilityBuilder =
+ apply { properties[PropertyMapStrings.DURATION.key] = duration }
+
+ fun setCheckInTime(checkInTime: Property<ZonedDateTime>): CapabilityBuilder =
+ apply { properties[PropertyMapStrings.CHECK_IN_TIME.key] = checkInTime }
+
override fun build(): Capability {
- // TODO(b/268369632): No-op remove empty property builder after Property od removed
- super.setProperty(Properties.Builder().build())
+ super.setProperty(properties)
return super.build()
}
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties internal constructor(
- val duration: Property<Duration>?,
- val checkInTime: Property<ZonedDateTime>?
- ) {
- override fun toString(): String {
- return "Property(duration=$duration, checkInTime=$checkInTime)"
- }
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as Properties
-
- if (duration != other.duration) return false
- if (checkInTime != other.checkInTime) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- var result = duration.hashCode()
- result = 31 * result + checkInTime.hashCode()
- return result
- }
-
- class Builder {
- private var duration: Property<Duration>? = null
-
- private var checkInTime: Property<ZonedDateTime>? = null
-
- fun setDuration(duration: Property<Duration>): Builder =
- apply { this.duration = duration }
-
- fun setCheckInTime(checkInTime: Property<ZonedDateTime>): Builder =
- apply { this.checkInTime = checkInTime }
-
- fun build(): Properties = Properties(duration, checkInTime)
- }
- }
-
class Arguments internal constructor(
val duration: Duration?,
val checkInTime: ZonedDateTime?
diff --git a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopEmergencySharing.kt b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopEmergencySharing.kt
index afc9f18..731aff5 100644
--- a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopEmergencySharing.kt
+++ b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopEmergencySharing.kt
@@ -20,11 +20,12 @@
import androidx.appactions.builtintypes.experimental.types.GenericErrorStatus
import androidx.appactions.builtintypes.experimental.types.NoInternetConnection
import androidx.appactions.builtintypes.experimental.types.SuccessStatus
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
+import androidx.appactions.interaction.capabilities.core.properties.Property
import androidx.appactions.interaction.capabilities.safety.executionstatus.SafetyAccountNotLoggedIn
import androidx.appactions.interaction.capabilities.safety.executionstatus.SafetyFeatureNotOnboarded
import androidx.appactions.interaction.proto.ParamValue
@@ -37,7 +38,6 @@
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(StopEmergencySharing.Properties::class.java)
.setArguments(
StopEmergencySharing.Arguments::class.java,
StopEmergencySharing.Arguments::Builder
@@ -55,17 +55,17 @@
// TODO(b/267805819): Update to include the SessionFactory once Session API is ready.
class CapabilityBuilder :
Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession,
+ CapabilityBuilder, Arguments, Output, Confirmation, ExecutionSession,
>(ACTION_SPEC) {
+
+ private var properties = mutableMapOf<String, Property<*>>()
+
override fun build(): Capability {
- super.setProperty(Properties())
+ super.setProperty(properties)
return super.build()
}
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties internal constructor()
-
class Arguments internal constructor() {
class Builder : BuilderOf<Arguments> {
override fun build(): Arguments = Arguments()
diff --git a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopSafetyCheck.kt b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopSafetyCheck.kt
index 3e5156b..3d3ffed 100644
--- a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopSafetyCheck.kt
+++ b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StopSafetyCheck.kt
@@ -20,11 +20,12 @@
import androidx.appactions.builtintypes.experimental.types.GenericErrorStatus
import androidx.appactions.builtintypes.experimental.types.NoInternetConnection
import androidx.appactions.builtintypes.experimental.types.SuccessStatus
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
+import androidx.appactions.interaction.capabilities.core.properties.Property
import androidx.appactions.interaction.capabilities.safety.executionstatus.SafetyAccountNotLoggedIn
import androidx.appactions.interaction.capabilities.safety.executionstatus.SafetyFeatureNotOnboarded
import androidx.appactions.interaction.proto.ParamValue
@@ -37,7 +38,6 @@
private val ACTION_SPEC =
ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(StopSafetyCheck.Properties::class.java)
.setArguments(StopSafetyCheck.Arguments::class.java, StopSafetyCheck.Arguments::Builder)
.setOutput(StopSafetyCheck.Output::class.java)
.bindOptionalOutput(
@@ -52,17 +52,16 @@
// TODO(b/267805819): Update to include the SessionFactory once Session API is ready.
class CapabilityBuilder :
Capability.Builder<
- CapabilityBuilder, Properties, Arguments, Output, Confirmation, ExecutionSession
+ CapabilityBuilder, Arguments, Output, Confirmation, ExecutionSession
>(ACTION_SPEC) {
+
+ private var properties = mutableMapOf<String, Property<*>>()
override fun build(): Capability {
- super.setProperty(Properties())
+ super.setProperty(properties)
return super.build()
}
}
- // TODO(b/268369632): Remove Property from public capability APIs.
- class Properties internal constructor()
-
class Arguments internal constructor() {
class Builder : BuilderOf<Arguments> {
override fun build(): Arguments = Arguments()
diff --git a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeCapability.kt b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeCapability.kt
index 3d03fc9..9708c4a 100644
--- a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeCapability.kt
+++ b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/testing/internal/FakeCapability.kt
@@ -16,26 +16,25 @@
package androidx.appactions.interaction.service.testing.internal
-import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
-import androidx.appactions.interaction.capabilities.core.HostProperties
+import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.ValueListener
import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.StringValue
-import androidx.appactions.interaction.capabilities.core.properties.Property
import androidx.appactions.interaction.capabilities.core.impl.task.SessionBridge
import androidx.appactions.interaction.capabilities.core.impl.task.TaskHandler
+import androidx.appactions.interaction.capabilities.core.properties.Property
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
import java.util.Optional
private const val CAPABILITY_NAME = "actions.intent.FAKE_CAPABILITY"
+@Suppress("UNCHECKED_CAST")
private val ACTION_SPEC = ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
- .setDescriptor(FakeCapability.Properties::class.java)
.setArguments(FakeCapability.Arguments::class.java, FakeCapability.Arguments::Builder)
.setOutput(FakeCapability.Output::class.java).bindOptionalParameter(
"fieldOne",
- { property -> Optional.ofNullable(property.fieldOne) },
+ { property -> Optional.ofNullable(property["fieldOne"] as Property<StringValue>) },
FakeCapability.Arguments.Builder::setFieldOne,
TypeConverters.STRING_PARAM_VALUE_CONVERTER,
TypeConverters.STRING_VALUE_ENTITY_CONVERTER,
@@ -69,7 +68,6 @@
class CapabilityBuilder : Capability.Builder<
CapabilityBuilder,
- Properties,
Arguments,
Output,
Confirmation,
@@ -94,12 +92,14 @@
this.fieldOne = fieldOne
}
- public override fun setExecutionSessionFactory(
- sessionFactory: (hostProperties: HostProperties?) -> ExecutionSession,
- ) = super.setExecutionSessionFactory(sessionFactory)
-
override fun build(): Capability {
- super.setProperty(Properties(fieldOne))
+ super.setProperty(
+ mutableMapOf(
+ "fieldOne" to (
+ fieldOne ?: Property.Builder<StringValue>().build()
+ )
+ )
+ )
return super.build()
}
}
diff --git a/appcompat/appcompat/build.gradle b/appcompat/appcompat/build.gradle
index 86dddcc..614c612 100644
--- a/appcompat/appcompat/build.gradle
+++ b/appcompat/appcompat/build.gradle
@@ -23,8 +23,8 @@
api("androidx.fragment:fragment:1.5.4")
api(project(":appcompat:appcompat-resources"))
api("androidx.drawerlayout:drawerlayout:1.0.0")
- implementation(projectOrArtifact(":lifecycle:lifecycle-runtime"))
- implementation("androidx.lifecycle:lifecycle-viewmodel:2.5.1")
+ implementation("androidx.lifecycle:lifecycle-runtime:2.6.1")
+ implementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
implementation("androidx.profileinstaller:profileinstaller:1.3.0")
implementation("androidx.resourceinspection:resourceinspection-annotation:1.0.1")
api("androidx.savedstate:savedstate:1.2.1")
@@ -44,8 +44,8 @@
androidTestImplementation(libs.espressoCore, excludes.espresso)
androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it's own MockMaker
androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it's own MockMaker
- androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-viewmodel"))
- androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.5.1", {
+ androidTestImplementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
+ androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.6.1", {
// Needed to ensure that the same version of lifecycle-runtime-ktx
// is pulled into main and androidTest configurations. Otherwise,
// potentially leads to confusing errors about resolution
diff --git a/appcompat/integration-tests/receive-content-testapp/build.gradle b/appcompat/integration-tests/receive-content-testapp/build.gradle
index b365be4..8d74ce9 100644
--- a/appcompat/integration-tests/receive-content-testapp/build.gradle
+++ b/appcompat/integration-tests/receive-content-testapp/build.gradle
@@ -34,7 +34,7 @@
implementation(projectOrArtifact(":recyclerview:recyclerview"))
implementation(libs.material)
- androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-common"))
+ androidTestImplementation("androidx.lifecycle:lifecycle-common:2.6.1")
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testRules)
diff --git a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/task/MergeBaselineProfileTask.kt b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/task/MergeBaselineProfileTask.kt
index 7067cde0..cb5582f 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/task/MergeBaselineProfileTask.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/task/MergeBaselineProfileTask.kt
@@ -232,7 +232,12 @@
.file(BASELINE_PROFILE_FILENAME)
.get()
.asFile
- .writeText(filteredProfileRules.joinToString(System.lineSeparator()))
+ .apply {
+ delete()
+ if (filteredProfileRules.isNotEmpty()) {
+ writeText(filteredProfileRules.joinToString(System.lineSeparator()))
+ }
+ }
// If this is a library we can stop here and don't manage the startup profiles.
if (library.get()) {
@@ -243,6 +248,17 @@
val startupRules = baselineProfileFileCollection.files
.readLines { FILENAME_MATCHER_STARTUP_PROFILE in it.name }
+ if (variantName.isPresent && startupRules.isEmpty()) {
+ logger.warn(
+ """
+ No startup profile rules were generated for the variant `${variantName.get()}`.
+ This is most likely because there are no instrumentation test with baseline profile
+ rule, which specify `includeInStartupProfile = true`. If this is not intentional
+ check that tests for this variant exist in the `baselineProfile` dependency module.
+ """.trimIndent()
+ )
+ }
+
// Use same sorting without filter for startup profiles.
val sortedProfileRules = startupRules
.asSequence()
@@ -258,7 +274,12 @@
.file(STARTUP_PROFILE_FILENAME)
.get()
.asFile
- .writeText(sortedProfileRules.joinToString(System.lineSeparator()))
+ .apply {
+ delete()
+ if (sortedProfileRules.isNotEmpty()) {
+ writeText(sortedProfileRules.joinToString(System.lineSeparator()))
+ }
+ }
}
private fun Pair<RuleType, String>.isInclude(): Boolean = first == RuleType.INCLUDE
diff --git a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPluginTest.kt b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPluginTest.kt
index 69455db..a7e1cdf 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPluginTest.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPluginTest.kt
@@ -151,6 +151,32 @@
}
@Test
+ fun testGenerateTaskWithNoFlavorsForApplicationAndNoStartupProfile() {
+ projectSetup.consumer.setup(
+ androidPlugin = ANDROID_APPLICATION_PLUGIN
+ )
+ projectSetup.producer.setupWithoutFlavors(
+ releaseProfileLines = listOf(
+ Fixtures.CLASS_1_METHOD_1,
+ Fixtures.CLASS_1,
+ ),
+ releaseStartupProfileLines = listOf()
+ )
+
+ gradleRunner
+ .withArguments("generateBaselineProfile", "--stacktrace")
+ .build()
+
+ assertThat(readBaselineProfileFileContent("release"))
+ .containsExactly(
+ Fixtures.CLASS_1,
+ Fixtures.CLASS_1_METHOD_1,
+ )
+
+ assertThat(startupProfileFile("release").exists()).isFalse()
+ }
+
+ @Test
fun testGenerateTaskWithFlavorsAndDefaultMerge() {
projectSetup.consumer.setup(
androidPlugin = ANDROID_APPLICATION_PLUGIN,
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Outputs.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Outputs.kt
index ace3165..959758d 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Outputs.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Outputs.kt
@@ -30,14 +30,14 @@
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public object Outputs {
+object Outputs {
private val formatter: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss")
/**
* The intended output directory that respects the `additionalTestOutputDir`.
*/
- public val outputDirectory: File
+ val outputDirectory: File
/**
* The usable output directory, given permission issues with `adb shell` on Android R.
@@ -46,7 +46,7 @@
* This dir can be read/written by app
* This dir can be read by shell (see [forceFilesForShellAccessible] for API 21/22!)
*/
- public val dirUsableByAppAndShell: File
+ val dirUsableByAppAndShell: File
/**
* Any file created by this process for the shell to use must be explicitly made filesystem
@@ -69,10 +69,12 @@
// Media directory. (b/216588251)
context.getFirstMountedMediaDir()
}
+
Build.VERSION.SDK_INT <= 22 -> {
// prior to API 23, shell didn't have access to externalCacheDir
context.cacheDir
}
+
else -> context.externalCacheDir
} ?: throw IllegalStateException(
"Unable to select a directory for writing files, " +
@@ -94,6 +96,9 @@
Log.d(BenchmarkState.TAG, "Output Directory: $outputDirectory")
outputDirectory.mkdirs()
+
+ // Clear all the existing files in the output directories
+ deleteFiles { true }
}
/**
@@ -104,7 +109,7 @@
*
* @return The absolute path of the output [File].
*/
- public fun writeFile(
+ fun writeFile(
fileName: String,
reportKey: String,
reportOnRunEndOnly: Boolean = false,
@@ -134,22 +139,22 @@
return destination.absolutePath
}
- public fun sanitizeFilename(filename: String): String {
+ fun sanitizeFilename(filename: String): String {
return filename
.replace(" ", "")
.replace("(", "[")
.replace(")", "]")
}
- public fun testOutputFile(filename: String): File {
+ fun testOutputFile(filename: String): File {
return File(outputDirectory, filename)
}
- public fun dateToFileName(date: Date = Date()): String {
+ fun dateToFileName(date: Date = Date()): String {
return formatter.format(date)
}
- public fun relativePathFor(path: String): String {
+ fun relativePathFor(path: String): String {
val hasOutputDirectoryPrefix = path.startsWith(outputDirectory.absolutePath)
val relativePath = when {
hasOutputDirectoryPrefix -> path.removePrefix("${outputDirectory.absolutePath}/")
@@ -160,4 +165,10 @@
}
return relativePath
}
+
+ fun deleteFiles(filterBlock: (File) -> (Boolean)) {
+ listOf(outputDirectory, dirUsableByAppAndShell)
+ .flatMap { it.listFiles(filterBlock)?.asList() ?: emptyList() }
+ .forEach { it.delete() }
+ }
}
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt
index 6e5d23e..4719b94 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt
@@ -122,8 +122,16 @@
val result = ShellImpl.executeCommandUnsafe("ls -l $path")
if (result.isBlank()) "" else result.split(Regex("\\s+"))[3]
}
- check(sum.isNotBlank()) {
- "Checksum for $path was blank"
+ if (sum.isBlank()) {
+ if (!ShellImpl.isSessionRooted) {
+ val lsOutput = ShellImpl.executeCommandUnsafe("ls -l $path")
+ throw IllegalStateException(
+ "Checksum for $path was blank. Adb session is not rooted, if root owns file, " +
+ "you may need to \"adb root\" and delete the file: $lsOutput"
+ )
+ } else {
+ throw IllegalStateException("Checksum for $path was blank.")
+ }
}
return sum
}
@@ -690,7 +698,8 @@
* Usage args: ```path/to/shellWrapper.sh <scriptFile> <stderrFile> [inputFile]```
*/
private val scriptWrapperPath = Shell.createRunnableExecutable(
- "shellWrapper.sh",
+ // use separate paths to prevent access errors after `adb unroot`
+ if (ShellImpl.isSessionRooted) "shellWrapper_root.sh" else "shellWrapper.sh",
"""
### shell script which passes in stdin as needed, and captures stderr in a file
# $1 == script content (not executable)
diff --git a/benchmark/benchmark-macro-junit4/api/current.txt b/benchmark/benchmark-macro-junit4/api/current.txt
index 0bcc71e..94459b9 100644
--- a/benchmark/benchmark-macro-junit4/api/current.txt
+++ b/benchmark/benchmark-macro-junit4/api/current.txt
@@ -4,7 +4,7 @@
@RequiresApi(28) public final class BaselineProfileRule implements org.junit.rules.TestRule {
ctor public BaselineProfileRule();
method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
- method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean>? filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+ method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
method public void collectBaselineProfile(String packageName, optional int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
diff --git a/benchmark/benchmark-macro-junit4/api/public_plus_experimental_current.txt b/benchmark/benchmark-macro-junit4/api/public_plus_experimental_current.txt
index bd292b7..f2b4517 100644
--- a/benchmark/benchmark-macro-junit4/api/public_plus_experimental_current.txt
+++ b/benchmark/benchmark-macro-junit4/api/public_plus_experimental_current.txt
@@ -4,12 +4,12 @@
@RequiresApi(28) public final class BaselineProfileRule implements org.junit.rules.TestRule {
ctor public BaselineProfileRule();
method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
- method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean>? filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+ method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
method public void collectBaselineProfile(String packageName, optional int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
method public void collectBaselineProfile(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
- method @androidx.benchmark.macro.ExperimentalStableBaselineProfilesApi public void collectStableBaselineProfile(String packageName, int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional boolean strictStability, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean>? filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+ method @androidx.benchmark.macro.ExperimentalStableBaselineProfilesApi public void collectStableBaselineProfile(String packageName, int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional boolean strictStability, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
method @androidx.benchmark.macro.ExperimentalStableBaselineProfilesApi public void collectStableBaselineProfile(String packageName, int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional boolean strictStability, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
method @androidx.benchmark.macro.ExperimentalStableBaselineProfilesApi public void collectStableBaselineProfile(String packageName, int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
method @androidx.benchmark.macro.ExperimentalStableBaselineProfilesApi public void collectStableBaselineProfile(String packageName, int maxIterations, optional int stableIterations, optional String? outputFilePrefix, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
diff --git a/benchmark/benchmark-macro-junit4/api/restricted_current.txt b/benchmark/benchmark-macro-junit4/api/restricted_current.txt
index 0bcc71e..94459b9 100644
--- a/benchmark/benchmark-macro-junit4/api/restricted_current.txt
+++ b/benchmark/benchmark-macro-junit4/api/restricted_current.txt
@@ -4,7 +4,7 @@
@RequiresApi(28) public final class BaselineProfileRule implements org.junit.rules.TestRule {
ctor public BaselineProfileRule();
method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
- method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean>? filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+ method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
method public void collectBaselineProfile(String packageName, optional int iterations, optional String? outputFilePrefix, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
method public void collectBaselineProfile(String packageName, optional int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
diff --git a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt
index 12dca06..8a0d906 100644
--- a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt
+++ b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt
@@ -125,7 +125,7 @@
iterations: Int = 3,
outputFilePrefix: String? = null,
includeInStartupProfile: Boolean = false,
- filterPredicate: ((String) -> Boolean)? = null,
+ filterPredicate: ((String) -> Boolean) = { true },
profileBlock: MacrobenchmarkScope.() -> Unit
) {
collectBaselineProfile(
@@ -170,7 +170,7 @@
outputFilePrefix: String? = null,
includeInStartupProfile: Boolean = false,
strictStability: Boolean = false,
- filterPredicate: ((String) -> Boolean)? = null,
+ filterPredicate: ((String) -> Boolean) = { true },
profileBlock: MacrobenchmarkScope.() -> Unit
) {
collectStableBaselineProfile(
diff --git a/benchmark/benchmark-macro/api/restricted_current.ignore b/benchmark/benchmark-macro/api/restricted_current.ignore
index 870da85..0e7f13d 100644
--- a/benchmark/benchmark-macro/api/restricted_current.ignore
+++ b/benchmark/benchmark-macro/api/restricted_current.ignore
@@ -1,6 +1,8 @@
// Baseline format: 1.0
RemovedClass: androidx.benchmark.macro.Api29Kt:
Removed class androidx.benchmark.macro.Api29Kt
+RemovedClass: androidx.benchmark.macro.BaselineProfilesKt:
+ Removed class androidx.benchmark.macro.BaselineProfilesKt
RemovedClass: androidx.benchmark.macro.FrameTimingGfxInfoMetric:
Removed class androidx.benchmark.macro.FrameTimingGfxInfoMetric
RemovedClass: androidx.benchmark.macro.IdeSummaryStringKt:
@@ -17,9 +19,5 @@
Removed class androidx.benchmark.macro.TagKt
-RemovedMethod: androidx.benchmark.macro.BaselineProfilesKt#collectBaselineProfile(String, String, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit>):
- Removed method androidx.benchmark.macro.BaselineProfilesKt.collectBaselineProfile(String,String,kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit>)
-
-
RemovedPackage: androidx.benchmark.macro.perfetto:
Removed package androidx.benchmark.macro.perfetto
diff --git a/benchmark/benchmark-macro/api/restricted_current.txt b/benchmark/benchmark-macro/api/restricted_current.txt
index a5eae3b..3954702 100644
--- a/benchmark/benchmark-macro/api/restricted_current.txt
+++ b/benchmark/benchmark-macro/api/restricted_current.txt
@@ -9,11 +9,6 @@
enum_constant public static final androidx.benchmark.macro.BaselineProfileMode UseIfAvailable;
}
- public final class BaselineProfilesKt {
- method @RequiresApi(28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void collectStableBaselineProfile(String uniqueName, String packageName, int stableIterations, int maxIterations, optional boolean strictStability, boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean>? filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
- method @RequiresApi(28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void collectStableBaselineProfile(String uniqueName, String packageName, int stableIterations, int maxIterations, boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean>? filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
- }
-
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class BatteryCharge {
method public boolean hasMinimumCharge(optional boolean throwOnMissingMetrics);
field public static final androidx.benchmark.macro.BatteryCharge INSTANCE;
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/BaselineProfilesTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/BaselineProfilesTest.kt
index 2c4eb75..8c5199a 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/BaselineProfilesTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/BaselineProfilesTest.kt
@@ -36,21 +36,19 @@
HSPLjava/io/DataOutputStream;->writeByte(I)V+]Ljava/io/OutputStream;missing_types
""".trimIndent()
- val filtered = filterProfileRulesToTargetP(profile, sortRules = false)
+ val filtered = filterProfileRulesToTargetP(profile, sortRules = false) { true }
assertEquals(filtered.lines().size, 1)
assertEquals("Landroidx/Foo/Bar;", filtered)
}
@Test
fun filterBaselineRulesWithSorting() {
- // https://youtrack.jetbrains.com/issue/KT-2425
- val dollar = "$"
val profile = """
HPLandroidx/lifecycle/Lifecycle${dollar}Event;->downFrom(Landroidx/lifecycle/Lifecycle${dollar}State;)Landroidx/lifecycle/Lifecycle${dollar}Event;
HSPLandroidx/lifecycle/ClassesInfoCache${dollar}CallbackInfo;-><init>(Ljava/util/Map;)V
HSPLandroidx/lifecycle/ClassesInfoCache${dollar}CallbackInfo;->invokeCallbacks(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle${dollar}Event;Ljava/lang/Object;)V
""".trimIndent()
- val sorted = filterProfileRulesToTargetP(profile, sortRules = true)
+ val sorted = filterProfileRulesToTargetP(profile, sortRules = true) { true }
assertEquals(sorted.lines().size, 3)
val expected = """
HSPLandroidx/lifecycle/ClassesInfoCache${dollar}CallbackInfo;-><init>(Ljava/util/Map;)V
@@ -61,6 +59,24 @@
}
@Test
+ fun filterBaselineRulesWithSortingAndCustomFilter() {
+ val profile = """
+ HPLandroidx/lifecycle/Lifecycle${dollar}Event;->downFrom(Landroidx/lifecycle/Lifecycle${dollar}State;)Landroidx/lifecycle/Lifecycle${dollar}Event;
+ HSPLandroidx/lifecycle/ClassesInfoCache${dollar}CallbackInfo;->invokeCallbacks(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle${dollar}Event;Ljava/lang/Object;)V
+ HSPLandroidx/lifecycle/ClassesInfoCache${dollar}CallbackInfo;-><init>(Ljava/util/Map;)V
+ """.trimIndent()
+ val sorted = filterProfileRulesToTargetP(profile, sortRules = true) {
+ it.startsWith("HSPL")
+ }
+ assertEquals(sorted.lines().size, 2)
+ val expected = """
+ HSPLandroidx/lifecycle/ClassesInfoCache${dollar}CallbackInfo;-><init>(Ljava/util/Map;)V
+ HSPLandroidx/lifecycle/ClassesInfoCache${dollar}CallbackInfo;->invokeCallbacks(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle${dollar}Event;Ljava/lang/Object;)V
+ """.trimIndent()
+ assertEquals(expected, sorted)
+ }
+
+ @Test
fun deviceSpecifier() {
if (DeviceInfo.isEmulator) {
assertEquals(deviceSpecifier, "-e ")
@@ -70,4 +86,8 @@
assertNotEquals(deviceSpecifier, "-s ")
}
}
+ companion object {
+ // https://youtrack.jetbrains.com/issue/KT-2425
+ const val dollar = "$"
+ }
}
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt
index a4adce9..f4771c8 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt
@@ -225,14 +225,16 @@
private fun validateShaderCache(empty: Boolean, packageName: String) {
val path = MacrobenchmarkScope.getShaderCachePath(packageName)
+
println("validating shader path $path")
val fileCount = Shell.executeScriptCaptureStdout("find $path -type f | wc -l")
.trim()
.toInt()
if (empty) {
- assertEquals(0, fileCount)
+ val files = Shell.executeScriptCaptureStdout("find $path -type f")
+ assertEquals(0, fileCount, "Expected 0 files in $path, saw $fileCount (files = $files)")
} else {
- assertNotEquals(0, fileCount)
+ assertNotEquals(0, fileCount, "Expected >0 files in $path, saw $fileCount")
}
}
@@ -280,6 +282,11 @@
}
@Test
+ fun dropShaderCacheRoot() = validateDropShaderCacheWithRoot {
+ assertTrue(dropShaderCacheRoot())
+ }
+
+ @Test
fun dropKernelPageCache() {
val scope = MacrobenchmarkScope(
Packages.TARGET,
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
index d19339e..7ce8f71 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
@@ -16,6 +16,7 @@
package androidx.benchmark.macro
+import android.annotation.SuppressLint
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
@@ -37,13 +38,12 @@
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@RequiresApi(28)
-@JvmOverloads
fun collectBaselineProfile(
uniqueName: String,
packageName: String,
iterations: Int = 3,
includeInStartupProfile: Boolean,
- filterPredicate: ((String) -> Boolean)?,
+ filterPredicate: ((String) -> Boolean),
profileBlock: MacrobenchmarkScope.() -> Unit,
) {
val scope = buildMacrobenchmarkScope(packageName)
@@ -83,12 +83,13 @@
runs a non-trivial amount of code.
""".trimIndent()
}
- // Filter
- val profile = filterProfileRulesToTargetP(unfilteredProfile, sortRules = true)
- // Report
+ val profile = filterProfileRulesToTargetP(
+ profile = unfilteredProfile,
+ sortRules = true,
+ filterPredicate = filterPredicate
+ )
reportResults(
profile = profile,
- filterPredicate = filterPredicate,
uniqueFilePrefix = uniqueName,
startTime = startTime,
includeInStartupProfile = includeInStartupProfile
@@ -103,9 +104,8 @@
* waiting until they are stable.
* @suppress
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@RequiresApi(28)
-@JvmOverloads
fun collectStableBaselineProfile(
uniqueName: String,
packageName: String,
@@ -113,7 +113,7 @@
maxIterations: Int,
strictStability: Boolean = false,
includeInStartupProfile: Boolean,
- filterPredicate: ((String) -> Boolean)?,
+ filterPredicate: ((String) -> Boolean),
profileBlock: MacrobenchmarkScope.() -> Unit
) {
val scope = buildMacrobenchmarkScope(packageName)
@@ -198,10 +198,13 @@
" invokes the target app, and runs a non-trivial amount of code"
}
- val profile = filterProfileRulesToTargetP(lastProfile, sortRules = true)
+ val profile = filterProfileRulesToTargetP(
+ profile = lastProfile,
+ sortRules = true,
+ filterPredicate = filterPredicate
+ )
reportResults(
profile = profile,
- filterPredicate = filterPredicate,
uniqueFilePrefix = uniqueName,
startTime = startTime,
includeInStartupProfile = includeInStartupProfile
@@ -230,6 +233,7 @@
/**
* Builds a function that can kill the target process using the provided [MacrobenchmarkScope].
*/
+@SuppressLint("BanThreadSleep")
private fun MacrobenchmarkScope.killProcessBlock(): () -> Unit {
val killProcessBlock = {
// When generating baseline profiles we want to default to using
@@ -246,14 +250,10 @@
*/
private fun reportResults(
profile: String,
- filterPredicate: ((String) -> Boolean)?,
uniqueFilePrefix: String,
startTime: Long,
includeInStartupProfile: Boolean
) {
- // Filter profile if necessary based on filters
- val filteredProfile = applyPackageFilters(profile, filterPredicate)
-
// Write a file with a timestamp to be able to disambiguate between runs with the same
// unique name.
@@ -272,10 +272,10 @@
)
}
- val absolutePath = Outputs.writeFile(fileName, reportKey) { it.writeText(filteredProfile) }
+ val absolutePath = Outputs.writeFile(fileName, reportKey) { it.writeText(profile) }
val tsAbsolutePath = Outputs.writeFile(tsFileName, "baseline-profile-ts") {
Log.d(TAG, "Pull Baseline Profile with: `adb pull \"${it.absolutePath}\" .`")
- it.writeText(filteredProfile)
+ it.writeText(profile)
}
val totalRunTime = System.nanoTime() - startTime
@@ -382,7 +382,11 @@
}
@VisibleForTesting
-internal fun filterProfileRulesToTargetP(profile: String, sortRules: Boolean = true): String {
+internal fun filterProfileRulesToTargetP(
+ profile: String,
+ sortRules: Boolean = true,
+ filterPredicate: ((String) -> Boolean)
+): String {
val rules = profile.lines()
var filteredRules = rules.filterNot { rule ->
// We want to filter out rules that are not supported on P. (b/216508418)
@@ -390,7 +394,8 @@
if (rule.startsWith("[")) { // Array qualifier
true
} else rule.contains("+") // Inline cache specifier
- }
+ }.filter(filterPredicate)
+
if (sortRules) {
filteredRules = filteredRules.mapNotNull { ProfileRule.parse(it) }
.sortedWith(ProfileRule.comparator)
@@ -399,14 +404,6 @@
return filteredRules.joinToString(separator = "\n")
}
-private fun applyPackageFilters(profile: String, filterPredicate: ((String) -> Boolean)?): String {
- return filterPredicate?.run {
- profile
- .lines()
- .filter(filterPredicate).joinToString(System.lineSeparator())
- } ?: profile
-}
-
private fun summaryRecord(record: Summary): String {
val summary = StringBuilder()
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/FrameStatsResult.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/FrameStatsResult.kt
index 761053b..d986ca0 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/FrameStatsResult.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/FrameStatsResult.kt
@@ -30,7 +30,7 @@
val lastFrameNs: Long?
) {
companion object {
- private val NAME_REGEX = Regex("(\\S+) \\(visibility=[0-9]+\\)")
+ private val NAME_REGEX = Regex("(\\S+) \\(visibility=\\d+\\)")
fun parse(frameStatsOutput: String): List<FrameStatsResult> {
return frameStatsOutput
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkScope.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkScope.kt
index 0d3280b..b8e6d86 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkScope.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkScope.kt
@@ -23,12 +23,11 @@
import androidx.annotation.RequiresApi
import androidx.benchmark.DeviceInfo
import androidx.benchmark.Shell
-import androidx.benchmark.macro.MacrobenchmarkScope.Companion.Api24Helper.shaderDir
+import androidx.benchmark.macro.MacrobenchmarkScope.Companion.Api24ContextHelper.createDeviceProtectedStorageContextCompat
import androidx.benchmark.macro.perfetto.forceTrace
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import androidx.tracing.trace
-import java.io.File
/**
* Provides access to common operations in app automation, such as killing the app,
@@ -242,18 +241,31 @@
public fun dropShaderCache() {
Log.d(TAG, "Dropping shader cache for $packageName")
val dropError = ProfileInstallBroadcast.dropShaderCache(packageName)
- if (dropError != null) {
- if (Shell.isSessionRooted()) {
- // fall back to root approach
- val path = getShaderCachePath(packageName)
- Shell.executeScriptSilent("find $path -type f | xargs rm")
- } else {
+ if (dropError != null && !DeviceInfo.isEmulator) {
+ if (!dropShaderCacheRoot()) {
throw IllegalStateException(dropError)
}
}
}
/**
+ * Returns true if rooted, and delete operation succeeded without error.
+ *
+ * Note that if no files are present in the shader dir, true will still be returned.
+ */
+ internal fun dropShaderCacheRoot(): Boolean {
+ if (Shell.isSessionRooted()) {
+ // fall back to root approach
+ val path = getShaderCachePath(packageName)
+
+ // Use -f to allow missing files, since app may not have generated shaders.
+ Shell.executeScriptSilent("find $path -type f | xargs rm -f")
+ return true
+ }
+ return false
+ }
+
+ /**
* Drop caches via setprop added in API 31
*
* Feature for dropping caches without root added in 31: https://r.android.com/1584525
@@ -312,21 +324,24 @@
val context = InstrumentationRegistry.getInstrumentation().context
// Shader paths sourced from ActivityThread.java
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- context.shaderDir
+ val shaderDirectory = if (Build.VERSION.SDK_INT >= 34) {
+ // U switched to cache dir, so it's not deleted on each app update
+ context.createDeviceProtectedStorageContextCompat().cacheDir
+ } else if (Build.VERSION.SDK_INT >= 24) {
+ // shaders started using device protected storage context once it was added in N
+ context.createDeviceProtectedStorageContextCompat().codeCacheDir
} else {
// getCodeCacheDir was added in L, but not used by platform for shaders until M
// as M is minApi of this library, that's all we support here
context.codeCacheDir
- }.absolutePath.replace(context.packageName, packageName)
+ }
+ return shaderDirectory.absolutePath.replace(context.packageName, packageName)
}
@RequiresApi(Build.VERSION_CODES.N)
- internal object Api24Helper {
- val Context.shaderDir: File
- get() =
- // shaders started using device protected storage context once it was added in N
- createDeviceProtectedStorageContext().codeCacheDir
+ internal object Api24ContextHelper {
+ fun Context.createDeviceProtectedStorageContextCompat(): Context =
+ createDeviceProtectedStorageContext()
}
}
}
diff --git a/benchmark/integration-tests/baselineprofile-consumer/src/release/generated/baselineProfiles/expected-startup-prof.txt b/benchmark/integration-tests/baselineprofile-consumer/src/release/generated/baselineProfiles/expected-startup-prof.txt
new file mode 100644
index 0000000..8ac4720
--- /dev/null
+++ b/benchmark/integration-tests/baselineprofile-consumer/src/release/generated/baselineProfiles/expected-startup-prof.txt
@@ -0,0 +1,538 @@
+Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;
+HSPLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;->$r8$lambda$CNqLK7smWTFjXaIfqGSDUWf8U50(Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;)V
+PLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;->$r8$lambda$G2Q0ZfVkJNYXyLJAm2IZ1xq3Lto(Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;Landroidx/profileinstaller/ProfileVerifier$CompilationStatus;)V
+HSPLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;-><init>()V
+HSPLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;->onCreate(Landroid/os/Bundle;)V
+PLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;->onResume$lambda$1$lambda$0(Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;Landroidx/profileinstaller/ProfileVerifier$CompilationStatus;)V
+HSPLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;->onResume$lambda$1(Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;)V
+HSPLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;->onResume()V
+PLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity$$ExternalSyntheticLambda0;-><init>(Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;Landroidx/profileinstaller/ProfileVerifier$CompilationStatus;)V
+PLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity$$ExternalSyntheticLambda0;->run()V
+Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity$$ExternalSyntheticLambda1;
+HSPLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity$$ExternalSyntheticLambda1;-><init>(Landroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity;)V
+HSPLandroidx/benchmark/integration/baselineprofile/consumer/EmptyActivity$$ExternalSyntheticLambda1;->run()V
+Landroidx/concurrent/futures/AbstractResolvableFuture;
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture;-><clinit>()V
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture;-><init>()V
+PLandroidx/concurrent/futures/AbstractResolvableFuture;->afterDone()V
+PLandroidx/concurrent/futures/AbstractResolvableFuture;->clearListeners(Landroidx/concurrent/futures/AbstractResolvableFuture$Listener;)Landroidx/concurrent/futures/AbstractResolvableFuture$Listener;
+PLandroidx/concurrent/futures/AbstractResolvableFuture;->complete(Landroidx/concurrent/futures/AbstractResolvableFuture;)V
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture;->get()Ljava/lang/Object;
+PLandroidx/concurrent/futures/AbstractResolvableFuture;->getDoneValue(Ljava/lang/Object;)Ljava/lang/Object;
+PLandroidx/concurrent/futures/AbstractResolvableFuture;->releaseWaiters()V
+PLandroidx/concurrent/futures/AbstractResolvableFuture;->set(Ljava/lang/Object;)Z
+Landroidx/concurrent/futures/AbstractResolvableFuture$AtomicHelper;
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$AtomicHelper;-><init>()V
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$AtomicHelper;-><init>(Landroidx/concurrent/futures/AbstractResolvableFuture$1;)V
+Landroidx/concurrent/futures/AbstractResolvableFuture$Listener;
+PLandroidx/concurrent/futures/AbstractResolvableFuture$Listener;-><clinit>()V
+PLandroidx/concurrent/futures/AbstractResolvableFuture$Listener;-><init>(Ljava/lang/Runnable;Ljava/util/concurrent/Executor;)V
+Landroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;-><init>(Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;)V
+PLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;->casListeners(Landroidx/concurrent/futures/AbstractResolvableFuture;Landroidx/concurrent/futures/AbstractResolvableFuture$Listener;Landroidx/concurrent/futures/AbstractResolvableFuture$Listener;)Z
+PLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;->casValue(Landroidx/concurrent/futures/AbstractResolvableFuture;Ljava/lang/Object;Ljava/lang/Object;)Z
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;->casWaiters(Landroidx/concurrent/futures/AbstractResolvableFuture;Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;)Z
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;->putNext(Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;)V
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;->putThread(Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;Ljava/lang/Thread;)V
+Landroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper$$ExternalSyntheticBackportWithForwarding0;
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper$$ExternalSyntheticBackportWithForwarding0;->m(Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z
+Landroidx/concurrent/futures/AbstractResolvableFuture$SetFuture;
+Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$Waiter;-><clinit>()V
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$Waiter;-><init>()V
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$Waiter;-><init>(Z)V
+HSPLandroidx/concurrent/futures/AbstractResolvableFuture$Waiter;->setNext(Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;)V
+PLandroidx/concurrent/futures/AbstractResolvableFuture$Waiter;->unpark()V
+Landroidx/concurrent/futures/ResolvableFuture;
+HSPLandroidx/concurrent/futures/ResolvableFuture;-><init>()V
+HSPLandroidx/concurrent/futures/ResolvableFuture;->create()Landroidx/concurrent/futures/ResolvableFuture;
+PLandroidx/concurrent/futures/ResolvableFuture;->set(Ljava/lang/Object;)Z
+Landroidx/constraintlayout/solver/ArrayLinkedVariables;
+HSPLandroidx/constraintlayout/solver/ArrayLinkedVariables;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/ArrayLinkedVariables;-><init>(Landroidx/constraintlayout/solver/ArrayRow;Landroidx/constraintlayout/solver/Cache;)V
+Landroidx/constraintlayout/solver/ArrayRow;
+HSPLandroidx/constraintlayout/solver/ArrayRow;-><init>()V
+HSPLandroidx/constraintlayout/solver/ArrayRow;-><init>(Landroidx/constraintlayout/solver/Cache;)V
+HSPLandroidx/constraintlayout/solver/ArrayRow;->addError(Landroidx/constraintlayout/solver/LinearSystem;I)Landroidx/constraintlayout/solver/ArrayRow;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->addSingleError(Landroidx/constraintlayout/solver/SolverVariable;I)Landroidx/constraintlayout/solver/ArrayRow;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->chooseSubject(Landroidx/constraintlayout/solver/LinearSystem;)Z
+HSPLandroidx/constraintlayout/solver/ArrayRow;->chooseSubjectInVariables(Landroidx/constraintlayout/solver/LinearSystem;)Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->createRowCentering(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;IFLandroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;I)Landroidx/constraintlayout/solver/ArrayRow;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->createRowEquals(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;I)Landroidx/constraintlayout/solver/ArrayRow;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->createRowGreaterThan(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;I)Landroidx/constraintlayout/solver/ArrayRow;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->createRowLowerThan(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;I)Landroidx/constraintlayout/solver/ArrayRow;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->ensurePositiveConstant()V
+HSPLandroidx/constraintlayout/solver/ArrayRow;->getKey()Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/ArrayRow;->hasKeyVariable()Z
+HSPLandroidx/constraintlayout/solver/ArrayRow;->isEmpty()Z
+HSPLandroidx/constraintlayout/solver/ArrayRow;->isNew(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/LinearSystem;)Z
+HSPLandroidx/constraintlayout/solver/ArrayRow;->pivot(Landroidx/constraintlayout/solver/SolverVariable;)V
+HSPLandroidx/constraintlayout/solver/ArrayRow;->reset()V
+HSPLandroidx/constraintlayout/solver/ArrayRow;->updateFromFinalVariable(Landroidx/constraintlayout/solver/LinearSystem;Landroidx/constraintlayout/solver/SolverVariable;Z)V
+HSPLandroidx/constraintlayout/solver/ArrayRow;->updateFromRow(Landroidx/constraintlayout/solver/ArrayRow;Z)V
+HSPLandroidx/constraintlayout/solver/ArrayRow;->updateFromSystem(Landroidx/constraintlayout/solver/LinearSystem;)V
+Landroidx/constraintlayout/solver/ArrayRow$ArrayRowVariables;
+Landroidx/constraintlayout/solver/Cache;
+HSPLandroidx/constraintlayout/solver/Cache;-><init>()V
+Landroidx/constraintlayout/solver/LinearSystem;
+HSPLandroidx/constraintlayout/solver/LinearSystem;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/LinearSystem;-><init>()V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->acquireSolverVariable(Landroidx/constraintlayout/solver/SolverVariable$Type;Ljava/lang/String;)Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/LinearSystem;->addCentering(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;IFLandroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;II)V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->addConstraint(Landroidx/constraintlayout/solver/ArrayRow;)V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->addEquality(Landroidx/constraintlayout/solver/SolverVariable;I)V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->addEquality(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;II)Landroidx/constraintlayout/solver/ArrayRow;
+HSPLandroidx/constraintlayout/solver/LinearSystem;->addGreaterThan(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;II)V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->addLowerThan(Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;II)V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->addRow(Landroidx/constraintlayout/solver/ArrayRow;)V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->addSingleError(Landroidx/constraintlayout/solver/ArrayRow;II)V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->computeValues()V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->createErrorVariable(ILjava/lang/String;)Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/LinearSystem;->createObjectVariable(Ljava/lang/Object;)Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/LinearSystem;->createRow()Landroidx/constraintlayout/solver/ArrayRow;
+HSPLandroidx/constraintlayout/solver/LinearSystem;->createSlackVariable()Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/LinearSystem;->enforceBFS(Landroidx/constraintlayout/solver/LinearSystem$Row;)I
+HSPLandroidx/constraintlayout/solver/LinearSystem;->getCache()Landroidx/constraintlayout/solver/Cache;
+HSPLandroidx/constraintlayout/solver/LinearSystem;->getMetrics()Landroidx/constraintlayout/solver/Metrics;
+HSPLandroidx/constraintlayout/solver/LinearSystem;->getObjectVariableValue(Ljava/lang/Object;)I
+HSPLandroidx/constraintlayout/solver/LinearSystem;->increaseTableSize()V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->minimize()V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->minimizeGoal(Landroidx/constraintlayout/solver/LinearSystem$Row;)V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->optimize(Landroidx/constraintlayout/solver/LinearSystem$Row;Z)I
+HSPLandroidx/constraintlayout/solver/LinearSystem;->releaseRows()V
+HSPLandroidx/constraintlayout/solver/LinearSystem;->reset()V
+Landroidx/constraintlayout/solver/LinearSystem$Row;
+Landroidx/constraintlayout/solver/LinearSystem$ValuesRow;
+HSPLandroidx/constraintlayout/solver/LinearSystem$ValuesRow;-><init>(Landroidx/constraintlayout/solver/LinearSystem;Landroidx/constraintlayout/solver/Cache;)V
+Landroidx/constraintlayout/solver/Pools$Pool;
+Landroidx/constraintlayout/solver/Pools$SimplePool;
+HSPLandroidx/constraintlayout/solver/Pools$SimplePool;-><init>(I)V
+HSPLandroidx/constraintlayout/solver/Pools$SimplePool;->acquire()Ljava/lang/Object;
+HSPLandroidx/constraintlayout/solver/Pools$SimplePool;->release(Ljava/lang/Object;)Z
+HSPLandroidx/constraintlayout/solver/Pools$SimplePool;->releaseAll([Ljava/lang/Object;I)V
+Landroidx/constraintlayout/solver/PriorityGoalRow;
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow;-><init>(Landroidx/constraintlayout/solver/Cache;)V
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow;->addError(Landroidx/constraintlayout/solver/SolverVariable;)V
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow;->addToGoal(Landroidx/constraintlayout/solver/SolverVariable;)V
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow;->clear()V
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow;->getPivotCandidate(Landroidx/constraintlayout/solver/LinearSystem;[Z)Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow;->removeGoal(Landroidx/constraintlayout/solver/SolverVariable;)V
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow;->updateFromRow(Landroidx/constraintlayout/solver/ArrayRow;Z)V
+Landroidx/constraintlayout/solver/PriorityGoalRow$GoalVariableAccessor;
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow$GoalVariableAccessor;-><init>(Landroidx/constraintlayout/solver/PriorityGoalRow;Landroidx/constraintlayout/solver/PriorityGoalRow;)V
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow$GoalVariableAccessor;->addToGoal(Landroidx/constraintlayout/solver/SolverVariable;F)Z
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow$GoalVariableAccessor;->init(Landroidx/constraintlayout/solver/SolverVariable;)V
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow$GoalVariableAccessor;->isNegative()Z
+HSPLandroidx/constraintlayout/solver/PriorityGoalRow$GoalVariableAccessor;->reset()V
+Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/SolverVariable;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/SolverVariable;-><init>(Landroidx/constraintlayout/solver/SolverVariable$Type;Ljava/lang/String;)V
+HSPLandroidx/constraintlayout/solver/SolverVariable;->addToRow(Landroidx/constraintlayout/solver/ArrayRow;)V
+HSPLandroidx/constraintlayout/solver/SolverVariable;->increaseErrorId()V
+HSPLandroidx/constraintlayout/solver/SolverVariable;->removeFromRow(Landroidx/constraintlayout/solver/ArrayRow;)V
+HSPLandroidx/constraintlayout/solver/SolverVariable;->reset()V
+HSPLandroidx/constraintlayout/solver/SolverVariable;->setFinalValue(Landroidx/constraintlayout/solver/LinearSystem;F)V
+HSPLandroidx/constraintlayout/solver/SolverVariable;->setType(Landroidx/constraintlayout/solver/SolverVariable$Type;Ljava/lang/String;)V
+HSPLandroidx/constraintlayout/solver/SolverVariable;->updateReferencesWithNewDefinition(Landroidx/constraintlayout/solver/ArrayRow;)V
+Landroidx/constraintlayout/solver/SolverVariable$Type;
+HSPLandroidx/constraintlayout/solver/SolverVariable$Type;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/SolverVariable$Type;-><init>(Ljava/lang/String;I)V
+Landroidx/constraintlayout/solver/SolverVariableValues;
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;-><init>(Landroidx/constraintlayout/solver/ArrayRow;Landroidx/constraintlayout/solver/Cache;)V
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->add(Landroidx/constraintlayout/solver/SolverVariable;FZ)V
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->addToHashMap(Landroidx/constraintlayout/solver/SolverVariable;I)V
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->addVariable(ILandroidx/constraintlayout/solver/SolverVariable;F)V
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->clear()V
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->divideByAmount(F)V
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->findEmptySlot()I
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->get(Landroidx/constraintlayout/solver/SolverVariable;)F
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->getCurrentSize()I
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->getVariable(I)Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->getVariableValue(I)F
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->indexOf(Landroidx/constraintlayout/solver/SolverVariable;)I
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->insertVariable(ILandroidx/constraintlayout/solver/SolverVariable;F)V
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->invert()V
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->put(Landroidx/constraintlayout/solver/SolverVariable;F)V
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->remove(Landroidx/constraintlayout/solver/SolverVariable;Z)F
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->removeFromHashMap(Landroidx/constraintlayout/solver/SolverVariable;)V
+HSPLandroidx/constraintlayout/solver/SolverVariableValues;->use(Landroidx/constraintlayout/solver/ArrayRow;Z)F
+Landroidx/constraintlayout/solver/widgets/Barrier;
+Landroidx/constraintlayout/solver/widgets/ChainHead;
+Landroidx/constraintlayout/solver/widgets/ConstraintAnchor;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;-><init>(Landroidx/constraintlayout/solver/widgets/ConstraintWidget;Landroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->connect(Landroidx/constraintlayout/solver/widgets/ConstraintAnchor;IIZ)Z
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->getMargin()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->getSolverVariable()Landroidx/constraintlayout/solver/SolverVariable;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->getTarget()Landroidx/constraintlayout/solver/widgets/ConstraintAnchor;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->isConnected()Z
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->reset()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;->resetSolverVariable(Landroidx/constraintlayout/solver/Cache;)V
+Landroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;-><init>(Ljava/lang/String;I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;->values()[Landroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;
+Landroidx/constraintlayout/solver/widgets/ConstraintWidget;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;-><init>()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->addAnchors()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->addFirst()Z
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->addToSolver(Landroidx/constraintlayout/solver/LinearSystem;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->applyConstraints(Landroidx/constraintlayout/solver/LinearSystem;ZZZZLandroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/SolverVariable;Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;ZLandroidx/constraintlayout/solver/widgets/ConstraintAnchor;Landroidx/constraintlayout/solver/widgets/ConstraintAnchor;IIIIFZZZZIIIIFZ)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->createObjectVariables(Landroidx/constraintlayout/solver/LinearSystem;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getAnchor(Landroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;)Landroidx/constraintlayout/solver/widgets/ConstraintAnchor;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getBaselineDistance()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getCompanionWidget()Ljava/lang/Object;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getDimensionBehaviour(I)Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getHeight()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getHorizontalDimensionBehaviour()Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getMinHeight()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getMinWidth()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getParent()Landroidx/constraintlayout/solver/widgets/ConstraintWidget;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getVerticalDimensionBehaviour()Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getVisibility()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getWidth()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getX()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->getY()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->immediateConnect(Landroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;Landroidx/constraintlayout/solver/widgets/ConstraintWidget;Landroidx/constraintlayout/solver/widgets/ConstraintAnchor$Type;II)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->isChainHead(I)Z
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->isInHorizontalChain()Z
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->isInVerticalChain()Z
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->reset()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->resetSolverVariables(Landroidx/constraintlayout/solver/Cache;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setBaselineDistance(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setCompanionWidget(Ljava/lang/Object;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setDimensionRatio(Ljava/lang/String;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setFrame(IIII)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setHasBaseline(Z)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setHeight(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setHorizontalBiasPercent(F)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setHorizontalChainStyle(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setHorizontalDimensionBehaviour(Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setHorizontalMatchStyle(IIIF)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setHorizontalWeight(F)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setInBarrier(IZ)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setMaxHeight(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setMaxWidth(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setMinHeight(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setMinWidth(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setParent(Landroidx/constraintlayout/solver/widgets/ConstraintWidget;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setVerticalBiasPercent(F)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setVerticalChainStyle(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setVerticalDimensionBehaviour(Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setVerticalMatchStyle(IIIF)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setVerticalWeight(F)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setVisibility(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setWidth(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setX(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->setY(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget;->updateFromSolver(Landroidx/constraintlayout/solver/LinearSystem;)V
+Landroidx/constraintlayout/solver/widgets/ConstraintWidget$1;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget$1;-><clinit>()V
+Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;-><init>(Ljava/lang/String;I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;->values()[Landroidx/constraintlayout/solver/widgets/ConstraintWidget$DimensionBehaviour;
+Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;-><init>()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->addChildrenToSolver(Landroidx/constraintlayout/solver/LinearSystem;)Z
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->getMeasurer()Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measurer;
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->getOptimizationLevel()I
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->invalidateGraph()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->invalidateMeasures()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->isHeightMeasuredTooSmall()Z
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->isWidthMeasuredTooSmall()Z
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->layout()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->measure(IIIIIIIII)J
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->optimizeFor(I)Z
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->resetChains()V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->setMeasurer(Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measurer;)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->setOptimizationLevel(I)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->setRtl(Z)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->updateChildrenFromSolver(Landroidx/constraintlayout/solver/LinearSystem;[Z)V
+HSPLandroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;->updateHierarchy()V
+Landroidx/constraintlayout/solver/widgets/Guideline;
+Landroidx/constraintlayout/solver/widgets/Helper;
+Landroidx/constraintlayout/solver/widgets/HelperWidget;
+Landroidx/constraintlayout/solver/widgets/Optimizer;
+HSPLandroidx/constraintlayout/solver/widgets/Optimizer;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/widgets/Optimizer;->checkMatchParent(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;Landroidx/constraintlayout/solver/LinearSystem;Landroidx/constraintlayout/solver/widgets/ConstraintWidget;)V
+HSPLandroidx/constraintlayout/solver/widgets/Optimizer;->enabled(II)Z
+Landroidx/constraintlayout/solver/widgets/VirtualLayout;
+Landroidx/constraintlayout/solver/widgets/WidgetContainer;
+HSPLandroidx/constraintlayout/solver/widgets/WidgetContainer;-><init>()V
+HSPLandroidx/constraintlayout/solver/widgets/WidgetContainer;->add(Landroidx/constraintlayout/solver/widgets/ConstraintWidget;)V
+HSPLandroidx/constraintlayout/solver/widgets/WidgetContainer;->removeAllChildren()V
+HSPLandroidx/constraintlayout/solver/widgets/WidgetContainer;->resetSolverVariables(Landroidx/constraintlayout/solver/Cache;)V
+Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure;
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure;-><init>(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;)V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure;->measure(Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measurer;Landroidx/constraintlayout/solver/widgets/ConstraintWidget;Z)Z
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure;->measureChildren(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;)V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure;->solveLinearSystem(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;Ljava/lang/String;II)V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure;->solverMeasure(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;IIIIIIIII)J
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure;->updateHierarchy(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;)V
+Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measure;
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measure;-><init>()V
+Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measurer;
+Landroidx/constraintlayout/solver/widgets/analyzer/Dependency;
+Landroidx/constraintlayout/solver/widgets/analyzer/DependencyGraph;
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/DependencyGraph;-><init>(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;)V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/DependencyGraph;->invalidateGraph()V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/DependencyGraph;->invalidateMeasures()V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/DependencyGraph;->setMeasurer(Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measurer;)V
+Landroidx/constraintlayout/solver/widgets/analyzer/DependencyNode;
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/DependencyNode;-><init>(Landroidx/constraintlayout/solver/widgets/analyzer/WidgetRun;)V
+Landroidx/constraintlayout/solver/widgets/analyzer/DependencyNode$Type;
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/DependencyNode$Type;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/DependencyNode$Type;-><init>(Ljava/lang/String;I)V
+Landroidx/constraintlayout/solver/widgets/analyzer/DimensionDependency;
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/DimensionDependency;-><init>(Landroidx/constraintlayout/solver/widgets/analyzer/WidgetRun;)V
+Landroidx/constraintlayout/solver/widgets/analyzer/HorizontalWidgetRun;
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/HorizontalWidgetRun;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/HorizontalWidgetRun;-><init>(Landroidx/constraintlayout/solver/widgets/ConstraintWidget;)V
+Landroidx/constraintlayout/solver/widgets/analyzer/VerticalWidgetRun;
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/VerticalWidgetRun;-><init>(Landroidx/constraintlayout/solver/widgets/ConstraintWidget;)V
+Landroidx/constraintlayout/solver/widgets/analyzer/WidgetRun;
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/WidgetRun;-><init>(Landroidx/constraintlayout/solver/widgets/ConstraintWidget;)V
+Landroidx/constraintlayout/solver/widgets/analyzer/WidgetRun$RunType;
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/WidgetRun$RunType;-><clinit>()V
+HSPLandroidx/constraintlayout/solver/widgets/analyzer/WidgetRun$RunType;-><init>(Ljava/lang/String;I)V
+Landroidx/constraintlayout/widget/ConstraintHelper;
+Landroidx/constraintlayout/widget/ConstraintLayout;
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->access$000(Landroidx/constraintlayout/widget/ConstraintLayout;)Ljava/util/ArrayList;
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->addView(Landroid/view/View;ILandroid/view/ViewGroup$LayoutParams;)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->applyConstraintsFromLayoutParams(ZLandroid/view/View;Landroidx/constraintlayout/solver/widgets/ConstraintWidget;Landroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;Landroid/util/SparseArray;)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->checkLayoutParams(Landroid/view/ViewGroup$LayoutParams;)Z
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->dispatchDraw(Landroid/graphics/Canvas;)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->generateLayoutParams(Landroid/util/AttributeSet;)Landroid/view/ViewGroup$LayoutParams;
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->generateLayoutParams(Landroid/util/AttributeSet;)Landroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->getPaddingWidth()I
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->getViewWidget(Landroid/view/View;)Landroidx/constraintlayout/solver/widgets/ConstraintWidget;
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->init(Landroid/util/AttributeSet;II)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->isRtl()Z
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->markHierarchyDirty()V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->onLayout(ZIIII)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->onMeasure(II)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->onViewAdded(Landroid/view/View;)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->requestLayout()V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->resolveMeasuredDimension(IIIIZZ)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->resolveSystem(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;III)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->setChildrenConstraints()V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->setSelfDimensionBehaviour(Landroidx/constraintlayout/solver/widgets/ConstraintWidgetContainer;IIII)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout;->updateHierarchy()Z
+Landroidx/constraintlayout/widget/ConstraintLayout$1;
+HSPLandroidx/constraintlayout/widget/ConstraintLayout$1;-><clinit>()V
+Landroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;
+HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;->resolveLayoutDirection(I)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;->validate()V
+Landroidx/constraintlayout/widget/ConstraintLayout$LayoutParams$Table;
+HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams$Table;-><clinit>()V
+Landroidx/constraintlayout/widget/ConstraintLayout$Measurer;
+HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;-><init>(Landroidx/constraintlayout/widget/ConstraintLayout;Landroidx/constraintlayout/widget/ConstraintLayout;)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;->captureLayoutInfos(IIIIII)V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;->didMeasures()V
+HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;->measure(Landroidx/constraintlayout/solver/widgets/ConstraintWidget;Landroidx/constraintlayout/solver/widgets/analyzer/BasicMeasure$Measure;)V
+Landroidx/constraintlayout/widget/ConstraintLayoutStates;
+Landroidx/constraintlayout/widget/ConstraintSet;
+Landroidx/constraintlayout/widget/Guideline;
+Landroidx/constraintlayout/widget/Placeholder;
+Landroidx/constraintlayout/widget/R$styleable;
+HSPLandroidx/constraintlayout/widget/R$styleable;-><clinit>()V
+Landroidx/constraintlayout/widget/VirtualLayout;
+Landroidx/core/app/CoreComponentFactory;
+HSPLandroidx/core/app/CoreComponentFactory;-><init>()V
+HSPLandroidx/core/app/CoreComponentFactory;->checkCompatWrapper(Ljava/lang/Object;)Ljava/lang/Object;
+HSPLandroidx/core/app/CoreComponentFactory;->instantiateActivity(Ljava/lang/ClassLoader;Ljava/lang/String;Landroid/content/Intent;)Landroid/app/Activity;
+HSPLandroidx/core/app/CoreComponentFactory;->instantiateApplication(Ljava/lang/ClassLoader;Ljava/lang/String;)Landroid/app/Application;
+HSPLandroidx/core/app/CoreComponentFactory;->instantiateProvider(Ljava/lang/ClassLoader;Ljava/lang/String;)Landroid/content/ContentProvider;
+PLandroidx/core/app/CoreComponentFactory;->instantiateReceiver(Ljava/lang/ClassLoader;Ljava/lang/String;Landroid/content/Intent;)Landroid/content/BroadcastReceiver;
+Landroidx/core/app/CoreComponentFactory$CompatWrapped;
+Landroidx/core/app/NavUtils$$ExternalSyntheticApiModelOutline0;
+HSPLandroidx/core/app/NavUtils$$ExternalSyntheticApiModelOutline0;->m$1(Landroidx/constraintlayout/widget/ConstraintLayout;)I
+HSPLandroidx/core/app/NavUtils$$ExternalSyntheticApiModelOutline0;->m$2(Landroidx/constraintlayout/widget/ConstraintLayout;)I
+HSPLandroidx/core/app/NavUtils$$ExternalSyntheticApiModelOutline0;->m(Landroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;)I
+HSPLandroidx/core/app/NavUtils$$ExternalSyntheticApiModelOutline0;->m(Landroidx/constraintlayout/widget/ConstraintLayout;)I
+Landroidx/core/os/TraceCompat$$ExternalSyntheticApiModelOutline0;
+HSPLandroidx/core/os/TraceCompat$$ExternalSyntheticApiModelOutline0;->m()Z
+Landroidx/profileinstaller/Encoding$$ExternalSyntheticApiModelOutline0;
+HSPLandroidx/profileinstaller/Encoding$$ExternalSyntheticApiModelOutline0;->m()Landroid/view/Choreographer;
+HSPLandroidx/profileinstaller/Encoding$$ExternalSyntheticApiModelOutline0;->m(Landroid/os/Looper;)Landroid/os/Handler;
+HSPLandroidx/profileinstaller/Encoding$$ExternalSyntheticApiModelOutline0;->m(Landroid/view/Choreographer;Landroid/view/Choreographer$FrameCallback;)V
+PLandroidx/profileinstaller/ProfileInstallReceiver;-><init>()V
+PLandroidx/profileinstaller/ProfileInstallReceiver;->onReceive(Landroid/content/Context;Landroid/content/Intent;)V
+PLandroidx/profileinstaller/ProfileInstallReceiver;->saveProfile(Landroidx/profileinstaller/ProfileInstaller$DiagnosticsCallback;)V
+PLandroidx/profileinstaller/ProfileInstallReceiver$$ExternalSyntheticLambda0;-><init>()V
+PLandroidx/profileinstaller/ProfileInstallReceiver$ResultDiagnostics;-><init>(Landroidx/profileinstaller/ProfileInstallReceiver;)V
+PLandroidx/profileinstaller/ProfileInstallReceiver$ResultDiagnostics;->onResultReceived(ILjava/lang/Object;)V
+PLandroidx/profileinstaller/ProfileInstaller;-><clinit>()V
+PLandroidx/profileinstaller/ProfileInstaller;->hasAlreadyWrittenProfileForThisInstall(Landroid/content/pm/PackageInfo;Ljava/io/File;Landroidx/profileinstaller/ProfileInstaller$DiagnosticsCallback;)Z
+PLandroidx/profileinstaller/ProfileInstaller;->writeProfile(Landroid/content/Context;)V
+PLandroidx/profileinstaller/ProfileInstaller;->writeProfile(Landroid/content/Context;Ljava/util/concurrent/Executor;Landroidx/profileinstaller/ProfileInstaller$DiagnosticsCallback;)V
+PLandroidx/profileinstaller/ProfileInstaller;->writeProfile(Landroid/content/Context;Ljava/util/concurrent/Executor;Landroidx/profileinstaller/ProfileInstaller$DiagnosticsCallback;Z)V
+PLandroidx/profileinstaller/ProfileInstaller$1;-><init>()V
+PLandroidx/profileinstaller/ProfileInstaller$1;->onResultReceived(ILjava/lang/Object;)V
+PLandroidx/profileinstaller/ProfileInstaller$2;-><init>()V
+PLandroidx/profileinstaller/ProfileInstaller$2;->onResultReceived(ILjava/lang/Object;)V
+Landroidx/profileinstaller/ProfileInstallerInitializer;
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer;-><init>()V
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer;->create(Landroid/content/Context;)Landroidx/profileinstaller/ProfileInstallerInitializer$Result;
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer;->create(Landroid/content/Context;)Ljava/lang/Object;
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer;->delayAfterFirstFrame(Landroid/content/Context;)V
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer;->dependencies()Ljava/util/List;
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer;->installAfterDelay(Landroid/content/Context;)V
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer;->lambda$delayAfterFirstFrame$0$androidx-profileinstaller-ProfileInstallerInitializer(Landroid/content/Context;)V
+PLandroidx/profileinstaller/ProfileInstallerInitializer;->lambda$installAfterDelay$1(Landroid/content/Context;)V
+PLandroidx/profileinstaller/ProfileInstallerInitializer;->lambda$writeInBackground$2(Landroid/content/Context;)V
+PLandroidx/profileinstaller/ProfileInstallerInitializer;->writeInBackground(Landroid/content/Context;)V
+Landroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda0;
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda0;-><init>(Landroid/content/Context;)V
+PLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda0;->run()V
+Landroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda1;
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda1;-><init>(Landroidx/profileinstaller/ProfileInstallerInitializer;Landroid/content/Context;)V
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda1;->run()V
+PLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda2;-><init>(Landroid/content/Context;)V
+PLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda2;->run()V
+Landroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl;
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl;->lambda$postFrameCallback$0(Ljava/lang/Runnable;J)V
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl;->postFrameCallback(Ljava/lang/Runnable;)V
+Landroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl$$ExternalSyntheticLambda2;
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl$$ExternalSyntheticLambda2;-><init>(Ljava/lang/Runnable;)V
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl$$ExternalSyntheticLambda2;->doFrame(J)V
+Landroidx/profileinstaller/ProfileInstallerInitializer$Handler28Impl;
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Handler28Impl;->createAsync(Landroid/os/Looper;)Landroid/os/Handler;
+Landroidx/profileinstaller/ProfileInstallerInitializer$Result;
+HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Result;-><init>()V
+Landroidx/profileinstaller/ProfileVerifier;
+HSPLandroidx/profileinstaller/ProfileVerifier;-><clinit>()V
+HSPLandroidx/profileinstaller/ProfileVerifier;->getCompilationStatusAsync()Lcom/google/common/util/concurrent/ListenableFuture;
+PLandroidx/profileinstaller/ProfileVerifier;->getPackageLastUpdateTime(Landroid/content/Context;)J
+PLandroidx/profileinstaller/ProfileVerifier;->setCompilationStatus(IZZ)Landroidx/profileinstaller/ProfileVerifier$CompilationStatus;
+PLandroidx/profileinstaller/ProfileVerifier;->writeProfileVerification(Landroid/content/Context;Z)Landroidx/profileinstaller/ProfileVerifier$CompilationStatus;
+PLandroidx/profileinstaller/ProfileVerifier$Api33Impl;->getPackageInfo(Landroid/content/pm/PackageManager;Landroid/content/Context;)Landroid/content/pm/PackageInfo;
+PLandroidx/profileinstaller/ProfileVerifier$Cache;-><init>(IIJJ)V
+PLandroidx/profileinstaller/ProfileVerifier$Cache;->writeOnFile(Ljava/io/File;)V
+Landroidx/profileinstaller/ProfileVerifier$CompilationStatus;
+PLandroidx/profileinstaller/ProfileVerifier$CompilationStatus;-><init>(IZZ)V
+PLandroidx/profileinstaller/ProfileVerifier$CompilationStatus;->getProfileInstallResultCode()I
+PLandroidx/profileinstaller/ProfileVerifier$CompilationStatus;->hasProfileEnqueuedForCompilation()Z
+PLandroidx/profileinstaller/ProfileVerifier$CompilationStatus;->isCompiledWithProfile()Z
+Landroidx/startup/AppInitializer;
+HSPLandroidx/startup/AppInitializer;-><clinit>()V
+HSPLandroidx/startup/AppInitializer;-><init>(Landroid/content/Context;)V
+HSPLandroidx/startup/AppInitializer;->discoverAndInitialize()V
+HSPLandroidx/startup/AppInitializer;->discoverAndInitialize(Landroid/os/Bundle;)V
+HSPLandroidx/startup/AppInitializer;->doInitialize(Ljava/lang/Class;Ljava/util/Set;)Ljava/lang/Object;
+HSPLandroidx/startup/AppInitializer;->getInstance(Landroid/content/Context;)Landroidx/startup/AppInitializer;
+Landroidx/startup/InitializationProvider;
+HSPLandroidx/startup/InitializationProvider;-><init>()V
+HSPLandroidx/startup/InitializationProvider;->onCreate()Z
+Landroidx/startup/Initializer;
+Landroidx/startup/R$string;
+Landroidx/tracing/Trace;
+HSPLandroidx/tracing/Trace;->beginSection(Ljava/lang/String;)V
+HSPLandroidx/tracing/Trace;->endSection()V
+HSPLandroidx/tracing/Trace;->isEnabled()Z
+Landroidx/tracing/TraceApi18Impl;
+HSPLandroidx/tracing/TraceApi18Impl;->beginSection(Ljava/lang/String;)V
+HSPLandroidx/tracing/TraceApi18Impl;->endSection()V
+Lcom/google/common/util/concurrent/ListenableFuture;
+PLkotlin/Pair;-><init>(Ljava/lang/Object;Ljava/lang/Object;)V
+PLkotlin/Pair;->component1()Ljava/lang/Object;
+PLkotlin/Pair;->component2()Ljava/lang/Object;
+PLkotlin/Pair;->getFirst()Ljava/lang/Object;
+PLkotlin/Pair;->getSecond()Ljava/lang/Object;
+PLkotlin/TuplesKt;->to(Ljava/lang/Object;Ljava/lang/Object;)Lkotlin/Pair;
+PLkotlin/collections/ArraysKt___ArraysJvmKt;->asList([Ljava/lang/Object;)Ljava/util/List;
+PLkotlin/collections/ArraysUtilJVM;->asList([Ljava/lang/Object;)Ljava/util/List;
+PLkotlin/collections/CollectionsKt__CollectionsKt;->getLastIndex(Ljava/util/List;)I
+PLkotlin/collections/CollectionsKt__CollectionsKt;->optimizeReadOnlyList(Ljava/util/List;)Ljava/util/List;
+PLkotlin/collections/CollectionsKt__IterablesKt;->collectionSizeOrDefault(Ljava/lang/Iterable;I)I
+PLkotlin/collections/CollectionsKt___CollectionsKt;->joinTo$default(Ljava/lang/Iterable;Ljava/lang/Appendable;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ILjava/lang/CharSequence;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/Appendable;
+PLkotlin/collections/CollectionsKt___CollectionsKt;->joinTo(Ljava/lang/Iterable;Ljava/lang/Appendable;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ILjava/lang/CharSequence;Lkotlin/jvm/functions/Function1;)Ljava/lang/Appendable;
+PLkotlin/collections/CollectionsKt___CollectionsKt;->minOrNull(Ljava/lang/Iterable;)Ljava/lang/Comparable;
+PLkotlin/collections/IntIterator;-><init>()V
+PLkotlin/internal/ProgressionUtilKt;->differenceModulo(III)I
+PLkotlin/internal/ProgressionUtilKt;->getProgressionLastElement(III)I
+PLkotlin/internal/ProgressionUtilKt;->mod(II)I
+Lkotlin/jvm/internal/Intrinsics;
+PLkotlin/jvm/internal/Intrinsics;->checkNotNull(Ljava/lang/Object;Ljava/lang/String;)V
+PLkotlin/jvm/internal/Intrinsics;->checkNotNullExpressionValue(Ljava/lang/Object;Ljava/lang/String;)V
+HSPLkotlin/jvm/internal/Intrinsics;->checkNotNullParameter(Ljava/lang/Object;Ljava/lang/String;)V
+PLkotlin/jvm/internal/Lambda;-><init>(I)V
+PLkotlin/ranges/IntProgression;-><clinit>()V
+PLkotlin/ranges/IntProgression;-><init>(III)V
+PLkotlin/ranges/IntProgression;->getFirst()I
+PLkotlin/ranges/IntProgression;->getLast()I
+PLkotlin/ranges/IntProgression;->getStep()I
+PLkotlin/ranges/IntProgression;->iterator()Ljava/util/Iterator;
+PLkotlin/ranges/IntProgression;->iterator()Lkotlin/collections/IntIterator;
+PLkotlin/ranges/IntProgression$Companion;-><init>()V
+PLkotlin/ranges/IntProgression$Companion;-><init>(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+PLkotlin/ranges/IntProgressionIterator;-><init>(III)V
+PLkotlin/ranges/IntProgressionIterator;->hasNext()Z
+PLkotlin/ranges/IntProgressionIterator;->nextInt()I
+PLkotlin/ranges/IntRange;-><clinit>()V
+PLkotlin/ranges/IntRange;-><init>(II)V
+PLkotlin/ranges/IntRange;->getEndInclusive()Ljava/lang/Integer;
+PLkotlin/ranges/IntRange;->getStart()Ljava/lang/Integer;
+PLkotlin/ranges/IntRange$Companion;-><init>()V
+PLkotlin/ranges/IntRange$Companion;-><init>(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+PLkotlin/ranges/RangesKt___RangesKt;->coerceAtLeast(II)I
+PLkotlin/ranges/RangesKt___RangesKt;->coerceAtMost(II)I
+PLkotlin/ranges/RangesKt___RangesKt;->coerceIn(III)I
+PLkotlin/ranges/RangesKt___RangesKt;->until(II)Lkotlin/ranges/IntRange;
+PLkotlin/sequences/SequencesKt___SequencesKt;->map(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence;
+PLkotlin/sequences/SequencesKt___SequencesKt;->toCollection(Lkotlin/sequences/Sequence;Ljava/util/Collection;)Ljava/util/Collection;
+PLkotlin/sequences/SequencesKt___SequencesKt;->toList(Lkotlin/sequences/Sequence;)Ljava/util/List;
+PLkotlin/sequences/SequencesKt___SequencesKt;->toMutableList(Lkotlin/sequences/Sequence;)Ljava/util/List;
+PLkotlin/sequences/TransformingSequence;-><init>(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)V
+PLkotlin/sequences/TransformingSequence;->access$getSequence$p(Lkotlin/sequences/TransformingSequence;)Lkotlin/sequences/Sequence;
+PLkotlin/sequences/TransformingSequence;->access$getTransformer$p(Lkotlin/sequences/TransformingSequence;)Lkotlin/jvm/functions/Function1;
+PLkotlin/sequences/TransformingSequence;->iterator()Ljava/util/Iterator;
+PLkotlin/sequences/TransformingSequence$iterator$1;-><init>(Lkotlin/sequences/TransformingSequence;)V
+PLkotlin/sequences/TransformingSequence$iterator$1;->hasNext()Z
+PLkotlin/sequences/TransformingSequence$iterator$1;->next()Ljava/lang/Object;
+PLkotlin/text/CharsKt__CharJVMKt;->isWhitespace(C)Z
+PLkotlin/text/DelimitedRangesSequence;-><init>(Ljava/lang/CharSequence;IILkotlin/jvm/functions/Function2;)V
+PLkotlin/text/DelimitedRangesSequence;->access$getGetNextMatch$p(Lkotlin/text/DelimitedRangesSequence;)Lkotlin/jvm/functions/Function2;
+PLkotlin/text/DelimitedRangesSequence;->access$getInput$p(Lkotlin/text/DelimitedRangesSequence;)Ljava/lang/CharSequence;
+PLkotlin/text/DelimitedRangesSequence;->access$getLimit$p(Lkotlin/text/DelimitedRangesSequence;)I
+PLkotlin/text/DelimitedRangesSequence;->access$getStartIndex$p(Lkotlin/text/DelimitedRangesSequence;)I
+PLkotlin/text/DelimitedRangesSequence;->iterator()Ljava/util/Iterator;
+PLkotlin/text/DelimitedRangesSequence$iterator$1;-><init>(Lkotlin/text/DelimitedRangesSequence;)V
+PLkotlin/text/DelimitedRangesSequence$iterator$1;->calcNext()V
+PLkotlin/text/DelimitedRangesSequence$iterator$1;->hasNext()Z
+PLkotlin/text/DelimitedRangesSequence$iterator$1;->next()Ljava/lang/Object;
+PLkotlin/text/DelimitedRangesSequence$iterator$1;->next()Lkotlin/ranges/IntRange;
+PLkotlin/text/StringsKt__AppendableKt;->appendElement(Ljava/lang/Appendable;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
+PLkotlin/text/StringsKt__IndentKt;->getIndentFunction$StringsKt__IndentKt(Ljava/lang/String;)Lkotlin/jvm/functions/Function1;
+PLkotlin/text/StringsKt__IndentKt;->indentWidth$StringsKt__IndentKt(Ljava/lang/String;)I
+PLkotlin/text/StringsKt__IndentKt;->replaceIndent(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+PLkotlin/text/StringsKt__IndentKt;->trimIndent(Ljava/lang/String;)Ljava/lang/String;
+PLkotlin/text/StringsKt__IndentKt$getIndentFunction$1;-><clinit>()V
+PLkotlin/text/StringsKt__IndentKt$getIndentFunction$1;-><init>()V
+PLkotlin/text/StringsKt__IndentKt$getIndentFunction$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
+PLkotlin/text/StringsKt__IndentKt$getIndentFunction$1;->invoke(Ljava/lang/String;)Ljava/lang/String;
+PLkotlin/text/StringsKt__StringsJVMKt;->isBlank(Ljava/lang/CharSequence;)Z
+PLkotlin/text/StringsKt__StringsJVMKt;->regionMatches(Ljava/lang/String;ILjava/lang/String;IIZ)Z
+PLkotlin/text/StringsKt__StringsKt;->access$findAnyOf(Ljava/lang/CharSequence;Ljava/util/Collection;IZZ)Lkotlin/Pair;
+PLkotlin/text/StringsKt__StringsKt;->findAnyOf$StringsKt__StringsKt(Ljava/lang/CharSequence;Ljava/util/Collection;IZZ)Lkotlin/Pair;
+PLkotlin/text/StringsKt__StringsKt;->getIndices(Ljava/lang/CharSequence;)Lkotlin/ranges/IntRange;
+PLkotlin/text/StringsKt__StringsKt;->getLastIndex(Ljava/lang/CharSequence;)I
+PLkotlin/text/StringsKt__StringsKt;->lineSequence(Ljava/lang/CharSequence;)Lkotlin/sequences/Sequence;
+PLkotlin/text/StringsKt__StringsKt;->lines(Ljava/lang/CharSequence;)Ljava/util/List;
+PLkotlin/text/StringsKt__StringsKt;->rangesDelimitedBy$StringsKt__StringsKt$default(Ljava/lang/CharSequence;[Ljava/lang/String;IZIILjava/lang/Object;)Lkotlin/sequences/Sequence;
+PLkotlin/text/StringsKt__StringsKt;->rangesDelimitedBy$StringsKt__StringsKt(Ljava/lang/CharSequence;[Ljava/lang/String;IZI)Lkotlin/sequences/Sequence;
+PLkotlin/text/StringsKt__StringsKt;->requireNonNegativeLimit(I)V
+PLkotlin/text/StringsKt__StringsKt;->splitToSequence$default(Ljava/lang/CharSequence;[Ljava/lang/String;ZIILjava/lang/Object;)Lkotlin/sequences/Sequence;
+PLkotlin/text/StringsKt__StringsKt;->splitToSequence(Ljava/lang/CharSequence;[Ljava/lang/String;ZI)Lkotlin/sequences/Sequence;
+PLkotlin/text/StringsKt__StringsKt;->substring(Ljava/lang/CharSequence;Lkotlin/ranges/IntRange;)Ljava/lang/String;
+PLkotlin/text/StringsKt__StringsKt$rangesDelimitedBy$2;-><init>(Ljava/util/List;Z)V
+PLkotlin/text/StringsKt__StringsKt$rangesDelimitedBy$2;->invoke(Ljava/lang/CharSequence;I)Lkotlin/Pair;
+PLkotlin/text/StringsKt__StringsKt$rangesDelimitedBy$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+PLkotlin/text/StringsKt__StringsKt$splitToSequence$1;-><init>(Ljava/lang/CharSequence;)V
+PLkotlin/text/StringsKt__StringsKt$splitToSequence$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
+PLkotlin/text/StringsKt__StringsKt$splitToSequence$1;->invoke(Lkotlin/ranges/IntRange;)Ljava/lang/String;
+PLkotlin/text/StringsKt___StringsKt;->drop(Ljava/lang/String;I)Ljava/lang/String;
\ No newline at end of file
diff --git a/benchmark/integration-tests/baselineprofile-test-utils/utils.gradle b/benchmark/integration-tests/baselineprofile-test-utils/utils.gradle
index b8a42a7..1ec437f 100644
--- a/benchmark/integration-tests/baselineprofile-test-utils/utils.gradle
+++ b/benchmark/integration-tests/baselineprofile-test-utils/utils.gradle
@@ -1,4 +1,3 @@
-import com.android.build.api.artifact.SingleArtifact
import org.gradle.work.DisableCachingByDefault
import static androidx.baselineprofile.gradle.utils.UtilsKt.camelCase
@@ -21,14 +20,23 @@
@TaskAction
void exec() {
+ File expectedFile = getExpectedFile().get().asFile
File actualFile = new File(actualFilePath.get())
+
+ if (!expectedFile.exists() && actualFile.exists()) {
+ throw new GradleException("No profile was expected in ${actualFile.absolutePath}.")
+ }
+ if (expectedFile.exists() && !actualFile.exists()) {
+ throw new GradleException("A profile was expected in ${actualFile.absolutePath}.")
+ }
+
if (!actualFile.exists()) {
throw new GradleException(
- "A baseline profile was expected in ${actualFile.absolutePath}."
+ "A profile was expected in ${actualFile.absolutePath}."
)
}
- def expectedIter = getExpectedFile().get().asFile.text.lines().iterator()
+ def expectedIter = expectedFile.text.lines().iterator()
def actualIter = actualFile.text.lines().iterator()
def lineCounter = 0
@@ -43,11 +51,9 @@
}
if (!diff.isEmpty()) {
- logger.error("Actual generated baseline profile differs from expected one: \n\t"
+ logger.error("Actual generated profile differs from expected one: \n\t"
+ diff.join("\n\t"))
- throw new GradleException(
- "Actual generated baseline profile differs from expected one."
- )
+ throw new GradleException("Actual generated profile differs from expected one.")
}
// This deletes the actual file since it's a test artifact
@@ -59,14 +65,20 @@
// present and have the same content.
def testTaskProviders = []
-def registerAssertTask(ArrayList<TaskProvider<Task>> testTaskProviders, String variantName) {
+def registerAssertTask(
+ ArrayList<TaskProvider<Task>> testTaskProviders,
+ String variantName,
+ String taskName,
+ String expectedFilename,
+ String filename
+) {
def expectedBaselineProfileSubDir = "generated/baselineProfiles"
def expectedFile = project
.layout
.projectDirectory
- .file("src/$variantName/$expectedBaselineProfileSubDir/expected-baseline-prof.txt")
+ .file("src/$variantName/$expectedBaselineProfileSubDir/${expectedFilename}.txt")
// If there is no expected file then skip testing this variant.
if (!expectedFile.asFile.exists()) {
@@ -74,14 +86,14 @@
}
def taskProvider = project.tasks.register(
- camelCase("test", variantName, "baselineProfileGeneration"),
+ camelCase("test", variantName, "${taskName}Generation"),
AssertEqualsAndCleanUpTask
) {
it.expectedFile.set(expectedFile)
it.actualFilePath.set(project
.layout
.projectDirectory
- .file("src/$variantName/$expectedBaselineProfileSubDir/baseline-prof.txt")
+ .file("src/$variantName/$expectedBaselineProfileSubDir/${filename}.txt")
.getAsFile()
.absolutePath)
@@ -95,7 +107,20 @@
// An assert task is defined per variant
androidComponents {
onVariants(selector().all()) { variant ->
- registerAssertTask(testTaskProviders, variant.name)
+ registerAssertTask(
+ testTaskProviders,
+ variant.name,
+ "baselineProfile",
+ "expected-baseline-prof",
+ "baseline-prof"
+ )
+ registerAssertTask(
+ testTaskProviders,
+ variant.name,
+ "startupProfile",
+ "expected-startup-prof",
+ "startup-prof"
+ )
}
}
diff --git a/biometric/biometric/src/main/res/values-te/strings.xml b/biometric/biometric/src/main/res/values-te/strings.xml
index 878b295..b10f71e 100644
--- a/biometric/biometric/src/main/res/values-te/strings.xml
+++ b/biometric/biometric/src/main/res/values-te/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/bluetooth/bluetooth/api/current.txt b/bluetooth/bluetooth/api/current.txt
index 5a47765..d682668 100644
--- a/bluetooth/bluetooth/api/current.txt
+++ b/bluetooth/bluetooth/api/current.txt
@@ -44,5 +44,32 @@
method @RequiresPermission("android.permission.BLUETOOTH_ADVERTISE") public kotlinx.coroutines.flow.Flow<java.lang.Integer> advertise(androidx.bluetooth.AdvertiseParams advertiseParams);
}
+ public final class ScanFilter {
+ ctor public ScanFilter(optional androidx.bluetooth.BluetoothAddress? deviceAddress, optional int manufacturerId, optional byte[]? manufacturerData, optional byte[]? manufacturerDataMask, optional java.util.UUID? serviceDataUuid, optional byte[]? serviceData, optional byte[]? serviceDataMask, optional java.util.UUID? serviceUuid, optional java.util.UUID? serviceUuidMask);
+ method public androidx.bluetooth.BluetoothAddress? getDeviceAddress();
+ method public byte[]? getManufacturerData();
+ method public byte[]? getManufacturerDataMask();
+ method public int getManufacturerId();
+ method public byte[]? getServiceData();
+ method public byte[]? getServiceDataMask();
+ method public java.util.UUID? getServiceDataUuid();
+ method public java.util.UUID? getServiceUuid();
+ method public java.util.UUID? getServiceUuidMask();
+ property public final androidx.bluetooth.BluetoothAddress? deviceAddress;
+ property public final byte[]? manufacturerData;
+ property public final byte[]? manufacturerDataMask;
+ property public final int manufacturerId;
+ property public final byte[]? serviceData;
+ property public final byte[]? serviceDataMask;
+ property public final java.util.UUID? serviceDataUuid;
+ property public final java.util.UUID? serviceUuid;
+ property public final java.util.UUID? serviceUuidMask;
+ field public static final androidx.bluetooth.ScanFilter.Companion Companion;
+ field public static final int MANUFACTURER_FILTER_NONE = -1; // 0xffffffff
+ }
+
+ public static final class ScanFilter.Companion {
+ }
+
}
diff --git a/bluetooth/bluetooth/api/public_plus_experimental_current.txt b/bluetooth/bluetooth/api/public_plus_experimental_current.txt
index 5a47765..d682668 100644
--- a/bluetooth/bluetooth/api/public_plus_experimental_current.txt
+++ b/bluetooth/bluetooth/api/public_plus_experimental_current.txt
@@ -44,5 +44,32 @@
method @RequiresPermission("android.permission.BLUETOOTH_ADVERTISE") public kotlinx.coroutines.flow.Flow<java.lang.Integer> advertise(androidx.bluetooth.AdvertiseParams advertiseParams);
}
+ public final class ScanFilter {
+ ctor public ScanFilter(optional androidx.bluetooth.BluetoothAddress? deviceAddress, optional int manufacturerId, optional byte[]? manufacturerData, optional byte[]? manufacturerDataMask, optional java.util.UUID? serviceDataUuid, optional byte[]? serviceData, optional byte[]? serviceDataMask, optional java.util.UUID? serviceUuid, optional java.util.UUID? serviceUuidMask);
+ method public androidx.bluetooth.BluetoothAddress? getDeviceAddress();
+ method public byte[]? getManufacturerData();
+ method public byte[]? getManufacturerDataMask();
+ method public int getManufacturerId();
+ method public byte[]? getServiceData();
+ method public byte[]? getServiceDataMask();
+ method public java.util.UUID? getServiceDataUuid();
+ method public java.util.UUID? getServiceUuid();
+ method public java.util.UUID? getServiceUuidMask();
+ property public final androidx.bluetooth.BluetoothAddress? deviceAddress;
+ property public final byte[]? manufacturerData;
+ property public final byte[]? manufacturerDataMask;
+ property public final int manufacturerId;
+ property public final byte[]? serviceData;
+ property public final byte[]? serviceDataMask;
+ property public final java.util.UUID? serviceDataUuid;
+ property public final java.util.UUID? serviceUuid;
+ property public final java.util.UUID? serviceUuidMask;
+ field public static final androidx.bluetooth.ScanFilter.Companion Companion;
+ field public static final int MANUFACTURER_FILTER_NONE = -1; // 0xffffffff
+ }
+
+ public static final class ScanFilter.Companion {
+ }
+
}
diff --git a/bluetooth/bluetooth/api/restricted_current.txt b/bluetooth/bluetooth/api/restricted_current.txt
index 5a47765..d682668 100644
--- a/bluetooth/bluetooth/api/restricted_current.txt
+++ b/bluetooth/bluetooth/api/restricted_current.txt
@@ -44,5 +44,32 @@
method @RequiresPermission("android.permission.BLUETOOTH_ADVERTISE") public kotlinx.coroutines.flow.Flow<java.lang.Integer> advertise(androidx.bluetooth.AdvertiseParams advertiseParams);
}
+ public final class ScanFilter {
+ ctor public ScanFilter(optional androidx.bluetooth.BluetoothAddress? deviceAddress, optional int manufacturerId, optional byte[]? manufacturerData, optional byte[]? manufacturerDataMask, optional java.util.UUID? serviceDataUuid, optional byte[]? serviceData, optional byte[]? serviceDataMask, optional java.util.UUID? serviceUuid, optional java.util.UUID? serviceUuidMask);
+ method public androidx.bluetooth.BluetoothAddress? getDeviceAddress();
+ method public byte[]? getManufacturerData();
+ method public byte[]? getManufacturerDataMask();
+ method public int getManufacturerId();
+ method public byte[]? getServiceData();
+ method public byte[]? getServiceDataMask();
+ method public java.util.UUID? getServiceDataUuid();
+ method public java.util.UUID? getServiceUuid();
+ method public java.util.UUID? getServiceUuidMask();
+ property public final androidx.bluetooth.BluetoothAddress? deviceAddress;
+ property public final byte[]? manufacturerData;
+ property public final byte[]? manufacturerDataMask;
+ property public final int manufacturerId;
+ property public final byte[]? serviceData;
+ property public final byte[]? serviceDataMask;
+ property public final java.util.UUID? serviceDataUuid;
+ property public final java.util.UUID? serviceUuid;
+ property public final java.util.UUID? serviceUuidMask;
+ field public static final androidx.bluetooth.ScanFilter.Companion Companion;
+ field public static final int MANUFACTURER_FILTER_NONE = -1; // 0xffffffff
+ }
+
+ public static final class ScanFilter.Companion {
+ }
+
}
diff --git a/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/ScanFilterTest.kt b/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/ScanFilterTest.kt
new file mode 100644
index 0000000..c34b9766
--- /dev/null
+++ b/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/ScanFilterTest.kt
@@ -0,0 +1,136 @@
+/*
+ * 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.bluetooth
+
+import java.util.UUID
+import junit.framework.TestCase.assertEquals
+import junit.framework.TestCase.assertNull
+import kotlin.test.assertFailsWith
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ScanFilterTest {
+
+ @Test
+ fun constructorWithDefaultParams() {
+ val scanFilter = ScanFilter()
+
+ assertNull(scanFilter.deviceAddress)
+ assertEquals(ScanFilter.MANUFACTURER_FILTER_NONE, scanFilter.manufacturerId)
+ assertNull(scanFilter.manufacturerData)
+ assertNull(scanFilter.manufacturerDataMask)
+ assertNull(scanFilter.serviceDataUuid)
+ assertNull(scanFilter.serviceData)
+ assertNull(scanFilter.serviceDataMask)
+ assertNull(scanFilter.serviceUuid)
+ assertNull(scanFilter.serviceUuidMask)
+ }
+
+ @Test
+ fun constructor() {
+ val deviceAddress = BluetoothAddress("00:01:02:03:04:05", AddressType.ADDRESS_TYPE_PUBLIC)
+ val manufacturerId = 1
+ val manufacturerData = "AA".toByteArray()
+ val manufacturerDataMask = "AB".toByteArray()
+ val serviceDataUuid = UUID.randomUUID()
+ val serviceData = "BA".toByteArray()
+ val serviceDataMask = "BB".toByteArray()
+ val serviceUuid = UUID.randomUUID()
+ val serviceUuidMask = UUID.randomUUID()
+
+ val scanFilter = ScanFilter(
+ deviceAddress = deviceAddress,
+ manufacturerId = manufacturerId,
+ manufacturerData = manufacturerData,
+ manufacturerDataMask = manufacturerDataMask,
+ serviceDataUuid = serviceDataUuid,
+ serviceData = serviceData,
+ serviceDataMask = serviceDataMask,
+ serviceUuid = serviceUuid,
+ serviceUuidMask = serviceUuidMask
+ )
+
+ assertEquals(deviceAddress, scanFilter.deviceAddress)
+ assertEquals(manufacturerId, scanFilter.manufacturerId)
+ assertEquals(manufacturerDataMask, scanFilter.manufacturerDataMask)
+ assertEquals(serviceDataUuid, scanFilter.serviceDataUuid)
+ assertEquals(serviceData, scanFilter.serviceData)
+ assertEquals(serviceDataMask, scanFilter.serviceDataMask)
+ assertEquals(serviceUuid, scanFilter.serviceUuid)
+ assertEquals(serviceUuidMask, scanFilter.serviceUuidMask)
+ }
+
+ @Test
+ fun constructorWithInvalidManufacturerId() {
+ val invalidManufacturerId = -2
+
+ assertFailsWith<IllegalArgumentException> {
+ ScanFilter(manufacturerId = invalidManufacturerId)
+ }
+ }
+
+ @Test
+ fun constructorWithNullManufacturerData_andNonNullMask() {
+ val manufacturerDataMask = "nonNullMask".toByteArray()
+
+ assertFailsWith<IllegalArgumentException> {
+ ScanFilter(manufacturerDataMask = manufacturerDataMask)
+ }
+ }
+
+ @Test
+ fun constructorWithInvalidManufacturerDataMaskSize() {
+ val manufacturerData = "array".toByteArray()
+ val manufacturerDataMask = "arrayOfDifferentSize".toByteArray()
+
+ assertFailsWith<IllegalArgumentException> {
+ ScanFilter(manufacturerData = manufacturerData,
+ manufacturerDataMask = manufacturerDataMask)
+ }
+ }
+
+ @Test
+ fun constructorWithNullServiceData_andNonNullMask() {
+ val serviceDataMask = "nonNullMask".toByteArray()
+
+ assertFailsWith<IllegalArgumentException> {
+ ScanFilter(serviceDataMask = serviceDataMask)
+ }
+ }
+
+ @Test
+ fun constructorWithInvalidServiceDataMaskSize() {
+ val serviceData = "array".toByteArray()
+ val serviceDataMask = "arrayOfDifferentSize".toByteArray()
+
+ assertFailsWith<IllegalArgumentException> {
+ ScanFilter(serviceData = serviceData,
+ serviceDataMask = serviceDataMask)
+ }
+ }
+
+ @Test
+ fun constructorWithNullServiceUuid_andNonNullMask() {
+ val serviceUuidMask = UUID.randomUUID()
+
+ assertFailsWith<IllegalArgumentException> {
+ ScanFilter(serviceUuidMask = serviceUuidMask)
+ }
+ }
+}
\ No newline at end of file
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanFilter.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanFilter.kt
new file mode 100644
index 0000000..8969db2
--- /dev/null
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanFilter.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.bluetooth
+
+import java.util.UUID
+
+/**
+ * Criteria for filtering result from Bluetooth LE scans. A ScanFilter allows clients to restrict
+ * scan results to only those that are of interest to them.
+ */
+class ScanFilter(
+ /* The scan filter for the remote device address. Null if filter is not set. */
+ val deviceAddress: BluetoothAddress? = null,
+
+ /* The scan filter for manufacturer id. MANUFACTURER_FILTER_NONE if filter is not set. */
+ val manufacturerId: Int = MANUFACTURER_FILTER_NONE,
+
+ /* The scan filter for manufacturer data. Null if filter is not set. */
+ val manufacturerData: ByteArray? = null,
+
+ /* The partial filter on manufacturerData. Null if filter is not set. */
+ val manufacturerDataMask: ByteArray? = null,
+
+ /* The scan filter for service data uuid. Null if filter is not set. */
+ val serviceDataUuid: UUID? = null,
+
+ /* The scan filter for service data. Null if filter is not set. */
+ val serviceData: ByteArray? = null,
+
+ /* The partial filter on service data. Null if filter is not set. */
+ val serviceDataMask: ByteArray? = null,
+
+ /* The scan filter for service uuid. Null if filter is not set. */
+ val serviceUuid: UUID? = null,
+
+ /* The partial filter on service uuid. Null if filter is not set. */
+ val serviceUuidMask: UUID? = null
+) {
+ companion object {
+ const val MANUFACTURER_FILTER_NONE: Int = -1
+ }
+
+ init {
+ if (manufacturerId < 0 && manufacturerId != MANUFACTURER_FILTER_NONE) {
+ throw IllegalArgumentException("invalid manufacturerId")
+ }
+
+ if (manufacturerDataMask != null) {
+ if (manufacturerData == null) {
+ throw IllegalArgumentException(
+ "manufacturerData is null while manufacturerDataMask is not null")
+ }
+
+ if (manufacturerData.size != manufacturerDataMask.size) {
+ throw IllegalArgumentException(
+ "size mismatch for manufacturerData and manufacturerDataMask")
+ }
+ }
+
+ if (serviceDataMask != null) {
+ if (serviceData == null) {
+ throw IllegalArgumentException(
+ "serviceData is null while serviceDataMask is not null")
+ }
+
+ if (serviceData.size != serviceDataMask.size) {
+ throw IllegalArgumentException(
+ "size mismatch for service data and service data mask")
+ }
+ }
+
+ if (serviceUuidMask != null && serviceUuid == null) {
+ throw IllegalArgumentException("uuid is null while uuidMask is not null")
+ }
+ }
+}
\ No newline at end of file
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt
index 76d6330..dc9536e 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt
@@ -16,6 +16,10 @@
package androidx.bluetooth.integration.testapp.ui.scanner
+// TODO(ofy) Migrate from androidx.bluetooth.integration.testapp.experimental.BluetoothLe to
+// androidx.bluetooth.BluetoothDevice once in place
+// TODO(ofy) Migrate from androidx.bluetooth.integration.testapp.experimental.BluetoothLe to
+// androidx.bluetooth.BluetoothLe once scan API is in place
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.os.Bundle
@@ -28,8 +32,6 @@
import androidx.bluetooth.integration.testapp.R
import androidx.bluetooth.integration.testapp.databinding.FragmentScannerBinding
import android.annotation.SuppressLint
-// TODO(ofy) Migrate to androidx.bluetooth.BluetoothDevice once in place
-// TODO(ofy) Migrate to androidx.bluetooth.BluetoothLe once scan API is in place
import androidx.bluetooth.integration.testapp.experimental.BluetoothLe
import androidx.bluetooth.integration.testapp.ui.common.getColor
import androidx.core.view.isVisible
diff --git a/buildSrc-tests/src/test/kotlin/androidx/build/KmpPlatformsTest.kt b/buildSrc-tests/src/test/kotlin/androidx/build/KmpPlatformsTest.kt
index c7e2ad8..c81ae66 100644
--- a/buildSrc-tests/src/test/kotlin/androidx/build/KmpPlatformsTest.kt
+++ b/buildSrc-tests/src/test/kotlin/androidx/build/KmpPlatformsTest.kt
@@ -23,63 +23,69 @@
@Test
fun withAnEmptyFlag_itReturnsTheDefaultValue() {
- assertThat(KmpFlagParser.parse("")).isEqualTo(setOf(KmpPlatform.JVM))
+ assertThat(KmpFlagParser.parse("")).isEqualTo(
+ setOf(KmpPlatform.JVM, KmpPlatform.DESKTOP)
+ )
}
@Test
fun withANullFlag_itReturnsTheDefaultValue() {
- assertThat(KmpFlagParser.parse(null)).isEqualTo(setOf(KmpPlatform.JVM))
+ assertThat(KmpFlagParser.parse(null)).isEqualTo(
+ setOf(KmpPlatform.JVM, KmpPlatform.DESKTOP)
+ )
}
@Test
fun withASingleDefaultPlatform_itParsesTheFlagCorrectly() {
- assertThat(KmpFlagParser.parse("+jvm")).isEqualTo(setOf(KmpPlatform.JVM))
+ assertThat(KmpFlagParser.parse("+jvm")).isEqualTo(
+ setOf(KmpPlatform.JVM, KmpPlatform.DESKTOP)
+ )
}
@Test
fun withNoPlatforms_itParsesTheFlagCorrectly() {
- assertThat(KmpFlagParser.parse("-jvm")).isEqualTo(emptySet<KmpPlatform>())
+ assertThat(KmpFlagParser.parse("-jvm,-desktop")).isEqualTo(emptySet<KmpPlatform>())
}
@Test
fun withASingleNonDefaultPlatform_itParsesTheFlagCorrectly() {
assertThat(KmpFlagParser.parse("+js")).isEqualTo(
- setOf(KmpPlatform.JVM, KmpPlatform.JS)
+ setOf(KmpPlatform.JVM, KmpPlatform.JS, KmpPlatform.DESKTOP)
)
}
@Test
fun withAMultiplePlatforms_itParsesTheFlagCorrectly() {
assertThat(KmpFlagParser.parse("+js,+mac")).isEqualTo(
- setOf(KmpPlatform.JVM, KmpPlatform.JS, KmpPlatform.MAC)
+ setOf(KmpPlatform.JVM, KmpPlatform.JS, KmpPlatform.MAC, KmpPlatform.DESKTOP)
)
}
@Test
fun withNegativeFlags_itParsesTheFlagCorrectly() {
assertThat(KmpFlagParser.parse("-jvm,+mac")).isEqualTo(
- setOf(KmpPlatform.MAC)
+ setOf(KmpPlatform.MAC, KmpPlatform.DESKTOP)
)
}
@Test
fun withTheNativeFlag_itParsesTheFlagCorrectly() {
assertThat(KmpFlagParser.parse("+native")).isEqualTo(
- setOf(KmpPlatform.JVM, KmpPlatform.MAC, KmpPlatform.LINUX)
+ setOf(KmpPlatform.JVM, KmpPlatform.MAC, KmpPlatform.LINUX, KmpPlatform.DESKTOP)
)
}
@Test
fun withMultipleFlagsIncludingTheNativeFlag_itParsesTheFlagCorrectly() {
assertThat(KmpFlagParser.parse("-jvm,+native,+js")).isEqualTo(
- setOf(KmpPlatform.JS, KmpPlatform.MAC, KmpPlatform.LINUX)
+ setOf(KmpPlatform.JS, KmpPlatform.MAC, KmpPlatform.LINUX, KmpPlatform.DESKTOP)
)
}
@Test
fun withRedundentFlags_itParsesTheFlagCorrectly() {
assertThat(KmpFlagParser.parse("-jvm,+native,+linux,+mac,+linux")).isEqualTo(
- setOf(KmpPlatform.MAC, KmpPlatform.LINUX)
+ setOf(KmpPlatform.MAC, KmpPlatform.LINUX, KmpPlatform.DESKTOP)
)
}
}
\ No newline at end of file
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
index 6c1b432..f6c6252 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
@@ -86,6 +86,17 @@
} else { null }
}
+ @JvmOverloads
+ fun desktop(
+ block: Action<KotlinJvmTarget>? = null
+ ): KotlinJvmTarget? {
+ return if (project.enableJvm()) {
+ kotlinExtension.jvm("desktop") {
+ block?.execute(this)
+ }
+ } else { null }
+ }
+
/**
* Configures all mac targets supported by AndroidX.
*/
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt b/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
index 03b36dc..753d7bc 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
@@ -231,9 +231,6 @@
// Disable until ag/19949626 goes in (b/261918265)
disable.add("MissingQuantity")
- // Disable new lint check so that we can land incrementally.
- disable.add("VisibleForTests")
-
// Provide stricter enforcement for project types intended to run on a device.
if (extension.type.compilationTarget == CompilationTarget.DEVICE) {
fatal.add("Assert")
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/VerifyDependencyVersionsTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/VerifyDependencyVersionsTask.kt
index e16b300..3cb172a 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/VerifyDependencyVersionsTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/VerifyDependencyVersionsTask.kt
@@ -206,6 +206,11 @@
// Don't check Hilt compile-only configurations
if (name.startsWith("hiltCompileOnly")) return false
+
+ // Don't check Desktop configurations since we don't publish them anyway
+ if (name.startsWith("desktop")) return false
+ if (name.startsWith("skiko")) return false
+
return true
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXKmpDocsImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXKmpDocsImplPlugin.kt
index b3be53b..d6a11f1 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXKmpDocsImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXKmpDocsImplPlugin.kt
@@ -23,6 +23,7 @@
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
+import org.gradle.api.attributes.Category
import org.gradle.api.attributes.LibraryElements
import org.gradle.api.model.ObjectFactory
import org.gradle.api.tasks.bundling.Zip
@@ -94,6 +95,10 @@
LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
objectFactory.named(ATTRIBUTE_NAME)
)
+ it.attribute(
+ Category.CATEGORY_ATTRIBUTE,
+ objectFactory.named(Category.DOCUMENTATION)
+ )
}
}
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/transform/ConfigureAarAsJar.kt b/buildSrc/private/src/main/kotlin/androidx/build/transform/ConfigureAarAsJar.kt
index 590be01..1b04cf7 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/transform/ConfigureAarAsJar.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/transform/ConfigureAarAsJar.kt
@@ -20,6 +20,7 @@
import org.gradle.api.Project
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.Usage
+import org.gradle.api.attributes.java.TargetJvmEnvironment
/**
* Creates `testAarAsJar` configuration that can be used for JVM tests that need to Android library
@@ -56,4 +57,11 @@
project.configurations.getByName(configurationName).dependencies.add(
project.dependencies.create(aarAsJar)
)
+
+ // Added to allow the :external:paparazzi:paparazzi build to select the correct jar (not get
+ // confused by the mpp jars) when the mpp builds are enabled
+ project.configurations.getByName(testAarsAsJars.name).attributes.attribute(
+ TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
+ project.objects.named(TargetJvmEnvironment::class.java, TargetJvmEnvironment.ANDROID)
+ )
}
\ No newline at end of file
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt b/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
index e43634f..5d3287f 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
@@ -147,11 +147,6 @@
"partiallyDejetifyArchive",
"stripArchiveForPartialDejetification",
"createArchive",
-
- // b/275795136
- ":room:integration-tests:room-testapp-kotlin:kspWithKspGenJavaDebugAndroidTestKotlin",
- // b/275795136
- ":room:integration-tests:room-testapp-kotlin:kspWithKspGenKotlinDebugAndroidTestKotlin"
)
val DONT_TRY_RERUNNING_TASK_TYPES = setOf(
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt b/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
index 802d8ef..7961e09 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
@@ -31,10 +31,11 @@
// https://blog.jetbrains.com/kotlin/2021/10/important-ua-parser-js-exploit-and-kotlin-js/
JS,
MAC,
- LINUX;
+ LINUX,
+ DESKTOP;
companion object {
val native = listOf(MAC, LINUX)
- val enabledByDefault = listOf(JVM)
+ val enabledByDefault = listOf(JVM, DESKTOP)
private const val JVM_PLATFORM = "jvm"
private const val JS_PLATFORM = "js"
private const val MAC_ARM_64 = "macosarm64"
@@ -43,6 +44,7 @@
private const val IOS_SIMULATOR_ARM_64 = "iossimulatorarm64"
private const val IOS_X_64 = "iosx64"
private const val IOS_ARM_64 = "iosarm64"
+ private const val DESKTOP_PLATFORM = "desktop"
val macPlatforms = listOf(MAC_ARM_64, MAC_OSX_64)
val linuxPlatforms = listOf(LINUX_64)
val iosPlatforms = listOf(IOS_SIMULATOR_ARM_64, IOS_ARM_64, IOS_X_64)
@@ -88,4 +90,5 @@
fun Project.enableLinux(): Boolean =
enabledKmpPlatforms().contains(KmpPlatform.LINUX) || Multiplatform.isKotlinNativeEnabled(this)
fun Project.enableJvm(): Boolean = enabledKmpPlatforms().contains(KmpPlatform.JVM)
+fun Project.enableDesktop(): Boolean = enabledKmpPlatforms().contains(KmpPlatform.DESKTOP)
fun Project.enableNative(): Boolean = enableMac() && enableLinux()
\ No newline at end of file
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/Multiplatform.kt b/buildSrc/public/src/main/kotlin/androidx/build/Multiplatform.kt
index 326c96f..554c2fdf 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/Multiplatform.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/Multiplatform.kt
@@ -40,6 +40,7 @@
@JvmStatic
fun isKotlinNativeEnabled(project: Project): Boolean {
return "KMP".equals(System.getenv()["ANDROIDX_PROJECTS"], ignoreCase = true) ||
+ "INFRAROGUE".equals(System.getenv()["ANDROIDX_PROJECTS"], ignoreCase = true) ||
ProjectLayoutType.isPlayground(project) ||
project.providers.gradleProperty("androidx.kmp.native.enabled")
.orNull?.toBoolean() == true
diff --git a/buildSrc/shared-dependencies.gradle b/buildSrc/shared-dependencies.gradle
index 1b67981..0625be3 100644
--- a/buildSrc/shared-dependencies.gradle
+++ b/buildSrc/shared-dependencies.gradle
@@ -6,6 +6,8 @@
// Gradle APIs
implementation(gradleApi())
compileOnly(findGradleKotlinDsl())
+ //noinspection UseTomlInstead,GradleDependency
+ implementation("com.android.tools:r8:8.1.41") // Temporary workaround for b/279807477
// Android Gradle Plugin APIs used by Stable AIDL
implementation(libs.androidGradlePluginApi)
diff --git a/busytown/androidx.sh b/busytown/androidx.sh
index 621ff22..ef344d5 100755
--- a/busytown/androidx.sh
+++ b/busytown/androidx.sh
@@ -22,6 +22,8 @@
-Pandroidx.enableComposeCompilerMetrics=true \
-Pandroidx.enableComposeCompilerReports=true \
-Pandroidx.constraints=true \
+ # If/when we enable desktop, enable VerifyDependencyVersionsTask.kt/shouldVerifyConfiguration
+ -Pandroidx.enabled.kmp.target.platforms=-desktop \
--no-daemon \
--profile "$@"; then
EXIT_VALUE=1
diff --git a/busytown/androidx_compose_multiplatform.sh b/busytown/androidx_compose_multiplatform.sh
index 5f39449..dd31e36 100755
--- a/busytown/androidx_compose_multiplatform.sh
+++ b/busytown/androidx_compose_multiplatform.sh
@@ -9,10 +9,15 @@
# b/235340662 don't verify dependency versions because we cannot pin to multiplatform deps
-./androidx.sh \
- -Pandroidx.compose.multiplatformEnabled=true \
- compileDebugAndroidTestSources \
- compileDebugSources \
- desktopTestClasses \
- -x verifyDependencyVersions \
- -Pandroidx.enableAffectedModuleDetection=false "$@"
+impl/build.sh buildOnServer createAllArchives checkExternalLicenses listTaskOutputs \
+ -Pandroidx.compose.multiplatformEnabled=true \
+ -Pandroidx.enableComposeCompilerMetrics=true \
+ -Pandroidx.enableComposeCompilerReports=true \
+ -Pandroidx.constraints=true \
+ --no-daemon \
+ --profile \
+ compileDebugAndroidTestSources \
+ compileDebugSources \
+ desktopTestClasses \
+ -x verifyDependencyVersions \
+ -Pandroidx.enableAffectedModuleDetection=false "$@"
diff --git a/busytown/androidx_multiplatform_linux.sh b/busytown/androidx_multiplatform_linux.sh
index 3f9d518..34e9b84 100755
--- a/busytown/androidx_multiplatform_linux.sh
+++ b/busytown/androidx_multiplatform_linux.sh
@@ -3,12 +3,12 @@
cd "$(dirname $0)"
# Builds all projects that support KMP except for Compose-specific projects which are already
-# covered by androidx_compose_multiplatform.sh
+# covered by androidx_compose_multiplatform.sh.
# Must be run on Linux
-# build just KMP projects. This will also enable native targets.
-export ANDROIDX_PROJECTS=KMP
+# build just INFRAROGUE projects. This will also enable native targets.
+export ANDROIDX_PROJECTS=INFRAROGUE # TODO: Switch from `INFRAROGUE` to `KMP`
# disable cache, NS does not allow it yet: b/235227707
export USE_ANDROIDX_REMOTE_BUILD_CACHE=false
diff --git a/busytown/androidx_multiplatform_mac.sh b/busytown/androidx_multiplatform_mac.sh
index 9f2dfbd..3373b9d 100755
--- a/busytown/androidx_multiplatform_mac.sh
+++ b/busytown/androidx_multiplatform_mac.sh
@@ -7,7 +7,7 @@
# Must be run on Mac
-export ANDROIDX_PROJECTS=KMP
+export ANDROIDX_PROJECTS=INFRAROGUE # TODO: Switch from `INFRAROGUE` to `KMP`
# disable GCP cache, these machines don't have credentials.
export USE_ANDROIDX_REMOTE_BUILD_CACHE=false
diff --git a/busytown/androidx_multiplatform_mac_host_tests.sh b/busytown/androidx_multiplatform_mac_host_tests.sh
index 8da2ed5..77eb725 100755
--- a/busytown/androidx_multiplatform_mac_host_tests.sh
+++ b/busytown/androidx_multiplatform_mac_host_tests.sh
@@ -6,7 +6,7 @@
# Must be run on Mac
-export ANDROIDX_PROJECTS=KMP
+export ANDROIDX_PROJECTS=INFRAROGUE # TODO: Switch from `INFRAROGUE` to `KMP`
# disable GCP cache, these machines don't have credentials.
export USE_ANDROIDX_REMOTE_BUILD_CACHE=false
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/Camera2CameraControlDeviceTest.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/Camera2CameraControlDeviceTest.kt
index d0fa9e3..fc3bf34 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/Camera2CameraControlDeviceTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/Camera2CameraControlDeviceTest.kt
@@ -48,11 +48,12 @@
import androidx.camera.camera2.pipe.integration.interop.CaptureRequestOptions
import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
import androidx.camera.camera2.pipe.testing.VerifyResultListener
+import androidx.camera.camera2.pipe.testing.toCameraControlAdapter
+import androidx.camera.camera2.pipe.testing.toCameraInfoAdapter
import androidx.camera.core.CameraControl
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.UseCase
-import androidx.camera.core.impl.CameraInfoInternal
import androidx.camera.core.internal.CameraUseCaseAdapter
import androidx.camera.testing.CameraUtil
import androidx.camera.testing.CameraXUtil
@@ -105,7 +106,7 @@
CameraSelector.LENS_FACING_BACK
).build()
camera = CameraUtil.createCameraUseCaseAdapter(context, cameraSelector)
- cameraControl = camera.cameraControl as CameraControlAdapter
+ cameraControl = camera.cameraControl.toCameraControlAdapter()
camera2CameraControl = cameraControl.camera2cameraControl
comboListener = camera2CameraControl.requestListener
}
@@ -438,7 +439,7 @@
Context.CAMERA_SERVICE
) as CameraManager
val characteristics = cameraManager.getCameraCharacteristics(
- (camera.cameraInfo as CameraInfoInternal).cameraId
+ camera.cameraInfo.toCameraInfoAdapter().cameraId
)
val maxDigitalZoom = characteristics.get(
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt
index a072374..a4eb05c 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt
@@ -61,6 +61,8 @@
import androidx.camera.camera2.pipe.integration.interop.CaptureRequestOptions
import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
import androidx.camera.camera2.pipe.testing.VerifyResultListener
+import androidx.camera.camera2.pipe.testing.toCameraControlAdapter
+import androidx.camera.camera2.pipe.testing.toCameraInfoAdapter
import androidx.camera.core.Camera
import androidx.camera.core.CameraControl
import androidx.camera.core.CameraSelector
@@ -70,7 +72,6 @@
import androidx.camera.core.Preview
import androidx.camera.core.SurfaceOrientedMeteringPointFactory
import androidx.camera.core.UseCase
-import androidx.camera.core.impl.CameraInfoInternal
import androidx.camera.core.impl.DeferrableSurface
import androidx.camera.core.impl.Quirks
import androidx.camera.core.impl.SessionConfig
@@ -147,7 +148,7 @@
CameraSelector.LENS_FACING_BACK
).build()
camera = CameraUtil.createCameraUseCaseAdapter(context, cameraSelector)
- cameraControl = camera.cameraControl as CameraControlAdapter
+ cameraControl = camera.cameraControl.toCameraControlAdapter()
comboListener = cameraControl.camera2cameraControl.requestListener
characteristics = CameraUtil.getCameraCharacteristics(
@@ -473,7 +474,7 @@
}
private fun Camera.getMaxSupportedZoomRatio(): Float {
- return cameraInfo.zoomState.value!!.maxZoomRatio
+ return cameraInfo.toCameraInfoAdapter().zoomState.value!!.maxZoomRatio
}
private suspend fun verifyRequestOptions() {
@@ -506,7 +507,7 @@
cameraSelector,
*useCases,
)
- cameraControl = camera.cameraControl as CameraControlAdapter
+ cameraControl = camera.cameraControl.toCameraControlAdapter()
}
private fun createFakeRecordingUseCase(): FakeUseCase {
@@ -618,7 +619,7 @@
}
private fun Camera.getCameraQuirks(): Quirks {
- return (cameraInfo as? CameraInfoInternal)?.cameraQuirks!!
+ return cameraInfo.toCameraInfoAdapter().cameraQuirks
}
private fun CameraCharacteristics.isAfModeSupported(
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CaptureConfigAdapterDeviceTest.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CaptureConfigAdapterDeviceTest.kt
index 3f62b210..c8d8825a 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CaptureConfigAdapterDeviceTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CaptureConfigAdapterDeviceTest.kt
@@ -24,6 +24,7 @@
import android.view.Surface
import androidx.annotation.RequiresApi
import androidx.camera.camera2.pipe.integration.adapter.CameraControlAdapter
+import androidx.camera.camera2.pipe.testing.toCameraControlAdapter
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.impl.CameraCaptureCallback
@@ -101,7 +102,7 @@
}
}
- cameraControl = camera!!.cameraControl as CameraControlAdapter
+ cameraControl = camera!!.cameraControl.toCameraControlAdapter()
}
@After
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/EvCompDeviceTest.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/EvCompDeviceTest.kt
index 092a7c6..1c7730d 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/EvCompDeviceTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/EvCompDeviceTest.kt
@@ -26,6 +26,7 @@
import androidx.camera.camera2.pipe.integration.impl.ComboRequestListener
import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
import androidx.camera.camera2.pipe.testing.VerifyResultListener
+import androidx.camera.camera2.pipe.testing.toCameraControlAdapter
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageCapture
@@ -92,7 +93,7 @@
CameraSelector.LENS_FACING_BACK
).build()
camera = CameraUtil.createCameraUseCaseAdapter(context, cameraSelector)
- cameraControl = camera.cameraControl as CameraControlAdapter
+ cameraControl = camera.cameraControl.toCameraControlAdapter()
@OptIn(ExperimentalCamera2Interop::class)
comboListener = cameraControl.camera2cameraControl.requestListener
@@ -267,6 +268,6 @@
}
},
)
- cameraControl = camera.cameraControl as CameraControlAdapter
+ cameraControl = camera.cameraControl.toCameraControlAdapter()
}
}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/TapToFocusDeviceTest.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/TapToFocusDeviceTest.kt
index 0366575..a1d918e 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/TapToFocusDeviceTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/TapToFocusDeviceTest.kt
@@ -26,6 +26,7 @@
import androidx.camera.camera2.pipe.integration.impl.ComboRequestListener
import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
import androidx.camera.camera2.pipe.testing.VerifyResultListener
+import androidx.camera.camera2.pipe.testing.toCameraControlAdapter
import androidx.camera.core.CameraSelector
import androidx.camera.core.FocusMeteringAction
import androidx.camera.core.ImageCapture
@@ -115,7 +116,7 @@
imageCapture
)
- cameraControl = camera.cameraControl as CameraControlAdapter
+ cameraControl = camera.cameraControl.toCameraControlAdapter()
@OptIn(ExperimentalCamera2Interop::class)
comboListener = cameraControl.camera2cameraControl.requestListener
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUtil.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUtil.kt
new file mode 100644
index 0000000..9a7f3fc
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUtil.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.testing
+
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.integration.adapter.CameraControlAdapter
+import androidx.camera.camera2.pipe.integration.adapter.CameraInfoAdapter
+import androidx.camera.core.CameraControl
+import androidx.camera.core.CameraInfo
+import androidx.camera.core.impl.CameraControlInternal
+import androidx.camera.core.impl.CameraInfoInternal
+
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+fun CameraControl.toCameraControlAdapter(): CameraControlAdapter {
+ return ((this as CameraControlInternal).implementation) as CameraControlAdapter
+}
+
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+fun CameraInfo.toCameraInfoAdapter(): CameraInfoAdapter {
+ return ((this as CameraInfoInternal).implementation) as CameraInfoAdapter
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
index c7f1e59..12acfb6 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
@@ -69,6 +69,10 @@
debug { "$this#close" }
}
+ override fun setActiveResumingMode(enabled: Boolean) {
+ useCaseManager.setActiveResumeMode(enabled)
+ }
+
override fun release(): ListenableFuture<Void> {
return threads.scope.launch { useCaseManager.close() }.asListenableFuture()
}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
index bc9620f..0cf7c6c 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
@@ -88,7 +88,8 @@
CameraDevice.TEMPLATE_PREVIEW
)
- CaptureType.VIDEO_CAPTURE -> sessionBuilder.setTemplateType(
+ CaptureType.VIDEO_CAPTURE,
+ CaptureType.STREAM_SHARING -> sessionBuilder.setTemplateType(
CameraDevice.TEMPLATE_RECORD
)
}
@@ -103,7 +104,8 @@
CaptureType.PREVIEW,
CaptureType.IMAGE_ANALYSIS,
- CaptureType.VIDEO_CAPTURE ->
+ CaptureType.VIDEO_CAPTURE,
+ CaptureType.STREAM_SHARING ->
captureBuilder.templateType = CameraDevice.TEMPLATE_RECORD
}
mutableConfig.insertOption(
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
index ddba7d3..5d79889 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
@@ -229,6 +229,7 @@
override fun setZslDisabled(disabled: Boolean) = this
override fun setHighResolutionDisabled(disabled: Boolean) = this
+ override fun setCaptureType(captureType: UseCaseConfigFactory.CaptureType) = this
override fun build(): MeteringRepeating {
return MeteringRepeating(cameraProperties, useCaseConfig, displayInfoManager)
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt
index a2457a4..32c7488 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt
@@ -72,6 +72,8 @@
priority: Config.OptionPriority = defaultOptionPriority,
): Deferred<Unit>
+ fun setActiveResumeMode(enabled: Boolean) {}
+
// Lifecycle
fun close(): Job
}
@@ -159,6 +161,10 @@
optionPriority = priority
)
+ override fun setActiveResumeMode(enabled: Boolean) {
+ useCaseGraphConfig.graph.isForeground = enabled
+ }
+
private fun UseCaseCameraRequestControl.setSessionConfigAsync(
sessionConfig: SessionConfig
): Deferred<Unit> = setConfigAsync(
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 9f39981..e0a02ad 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
@@ -86,6 +86,9 @@
@GuardedBy("lock")
private val activeUseCases = mutableSetOf<UseCase>()
+ @GuardedBy("lock")
+ private var activeResumeEnabled = false
+
private val meteringRepeating by lazy {
MeteringRepeating.Builder(
cameraProperties,
@@ -124,9 +127,7 @@
}
if (attachedUseCases.addAll(useCases)) {
- if (shouldAddRepeatingUseCase(getRunningUseCases())) {
- addRepeatingUseCase()
- } else {
+ if (!addOrRemoveRepeatingUseCase(getRunningUseCases())) {
refreshAttachedUseCases(attachedUseCases)
}
}
@@ -164,8 +165,7 @@
// TODO: We might only want to tear down when the number of attached use cases goes to
// zero. If a single UseCase is removed, we could deactivate it?
if (attachedUseCases.removeAll(useCases)) {
- if (shouldRemoveRepeatingUseCase(getRunningUseCases())) {
- removeRepeatingUseCase()
+ if (addOrRemoveRepeatingUseCase(getRunningUseCases())) {
return
}
refreshAttachedUseCases(attachedUseCases)
@@ -208,6 +208,11 @@
}
}
+ fun setActiveResumeMode(enabled: Boolean) = synchronized(lock) {
+ activeResumeEnabled = enabled
+ camera?.setActiveResumeMode(enabled)
+ }
+
suspend fun close() {
val closingJobs = synchronized(lock) {
if (attachedUseCases.isNotEmpty()) {
@@ -271,6 +276,7 @@
for (control in allControls) {
control.useCaseCamera = camera
}
+ camera?.setActiveResumeMode(activeResumeEnabled)
refreshRunningUseCases()
}
@@ -280,6 +286,25 @@
return attachedUseCases.intersect(activeUseCases)
}
+ /**
+ * Adds or removes repeating use case if needed.
+ *
+ * @param runningUseCases the set of currently running use cases
+ * @return true if repeating use cases is added or removed, false otherwise
+ */
+ @GuardedBy("lock")
+ private fun addOrRemoveRepeatingUseCase(runningUseCases: Set<UseCase>): Boolean {
+ if (shouldAddRepeatingUseCase(runningUseCases)) {
+ addRepeatingUseCase()
+ return true
+ }
+ if (shouldRemoveRepeatingUseCase(runningUseCases)) {
+ removeRepeatingUseCase()
+ return true
+ }
+ return false
+ }
+
@GuardedBy("lock")
private fun shouldAddRepeatingUseCase(runningUseCases: Set<UseCase>): Boolean {
val meteringRepeatingEnabled = attachedUseCases.contains(meteringRepeating)
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraControl.kt
index d692dbe..2ca7aeb 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraControl.kt
@@ -27,6 +27,7 @@
import androidx.camera.camera2.pipe.integration.impl.UseCaseCameraControl
import androidx.camera.camera2.pipe.integration.impl.UseCaseThreads
import androidx.camera.core.CameraControl
+import androidx.camera.core.impl.CameraControlInternal
import androidx.camera.core.impl.utils.futures.Futures
import androidx.core.util.Preconditions
import com.google.common.util.concurrent.ListenableFuture
@@ -186,11 +187,12 @@
*/
@JvmStatic
fun from(cameraControl: CameraControl): Camera2CameraControl {
+ var cameraControlImpl = (cameraControl as CameraControlInternal).implementation
Preconditions.checkArgument(
- cameraControl is CameraControlAdapter,
+ cameraControlImpl is CameraControlAdapter,
"CameraControl doesn't contain Camera2 implementation."
)
- return (cameraControl as CameraControlAdapter).camera2cameraControl
+ return (cameraControlImpl as CameraControlAdapter).camera2cameraControl
}
/**
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt
index 2372dd3..3094fc4 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt
@@ -23,6 +23,7 @@
import androidx.camera.camera2.pipe.integration.compat.workaround.getSafely
import androidx.camera.camera2.pipe.integration.impl.CameraProperties
import androidx.camera.core.CameraInfo
+import androidx.camera.core.impl.CameraInfoInternal
import androidx.core.util.Preconditions
/**
@@ -88,11 +89,12 @@
*/
@JvmStatic
fun from(@Suppress("UNUSED_PARAMETER") cameraInfo: CameraInfo): Camera2CameraInfo {
+ var cameraInfoImpl = (cameraInfo as CameraInfoInternal).implementation
Preconditions.checkArgument(
- cameraInfo is CameraInfoAdapter,
+ cameraInfoImpl is CameraInfoAdapter,
"CameraInfo doesn't contain Camera2 implementation."
)
- return (cameraInfo as CameraInfoAdapter).camera2CameraInfo
+ return (cameraInfoImpl as CameraInfoAdapter).camera2CameraInfo
}
/**
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
index 58e25df..9248cf6 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
@@ -186,6 +186,28 @@
}
@Test
+ fun onlyOneUseCaseCameraBuilt_whenAllUseCasesButImageCaptureDisabled() {
+ // Arrange
+ val useCaseCameraBuilder = FakeUseCaseCameraComponentBuilder()
+ val useCaseManager = createUseCaseManager(
+ useCaseCameraComponentBuilder = useCaseCameraBuilder
+ )
+
+ val preview = createPreview()
+ val imageCapture = createImageCapture()
+ useCaseManager.attach(listOf(preview, imageCapture))
+ useCaseManager.activate(preview)
+ useCaseManager.activate(imageCapture)
+ useCaseCameraBuilder.buildInvocationCount = 0
+
+ // Act
+ useCaseManager.deactivate(preview)
+
+ // Assert
+ assertThat(useCaseCameraBuilder.buildInvocationCount).isEqualTo(1)
+ }
+
+ @Test
fun meteringRepeatingDisabled_whenAllUseCasesDisabled() {
// Arrange
val useCaseManager = createUseCaseManager()
@@ -202,6 +224,26 @@
}
@Test
+ fun onlyOneUseCaseCameraBuilt_whenAllUseCasesDisabled() {
+ // Arrange
+ val useCaseCameraBuilder = FakeUseCaseCameraComponentBuilder()
+ val useCaseManager = createUseCaseManager(
+ useCaseCameraComponentBuilder = useCaseCameraBuilder
+ )
+
+ val imageCapture = createImageCapture()
+ useCaseManager.attach(listOf(imageCapture))
+ useCaseManager.activate(imageCapture)
+ useCaseCameraBuilder.buildInvocationCount = 0
+
+ // Act
+ useCaseManager.deactivate(imageCapture)
+
+ // Assert
+ assertThat(useCaseCameraBuilder.buildInvocationCount).isEqualTo(1)
+ }
+
+ @Test
fun onStateAttachedInvokedExactlyOnce_whenUseCaseAttachedAndMeteringRepeatingAdded() {
// Arrange
val useCaseManager = createUseCaseManager()
@@ -268,9 +310,11 @@
@Suppress("UNCHECKED_CAST", "PLATFORM_CLASS_MAPPED_TO_KOTLIN")
private fun createUseCaseManager(
controls: Set<UseCaseCameraControl> = emptySet(),
+ useCaseCameraComponentBuilder: FakeUseCaseCameraComponentBuilder =
+ FakeUseCaseCameraComponentBuilder(),
) = UseCaseManager(
cameraConfig = CameraConfig(CameraId("0")),
- builder = FakeUseCaseCameraComponentBuilder(),
+ builder = useCaseCameraComponentBuilder,
controls = controls as java.util.Set<UseCaseCameraControl>,
cameraProperties = FakeCameraProperties(),
camera2CameraControl = Camera2CameraControl.create(
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraph.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraph.kt
index 6e30f2d..a3bbc1e 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraph.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraph.kt
@@ -34,6 +34,7 @@
override val graphState: StateFlow<GraphState>
get() = throw NotImplementedError("Not used in testing")
+ override var isForeground = false
override suspend fun acquireSession(): CameraGraph.Session {
return fakeCameraGraphSession
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt
index ca33199..20f7342 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt
@@ -44,6 +44,8 @@
import kotlinx.coroutines.withTimeoutOrNull
class FakeUseCaseCameraComponentBuilder : UseCaseCameraComponent.Builder {
+ var buildInvocationCount = 0
+
private val cameraQuirks = CameraQuirks(
FakeCameraMetadata(),
StreamConfigurationMapCompat(null, OutputSizesCorrector(FakeCameraMetadata(), null))
@@ -57,6 +59,7 @@
}
override fun build(): UseCaseCameraComponent {
+ buildInvocationCount++
return FakeUseCaseCameraComponent(config.provideUseCaseList())
}
}
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 a8b6841..5ce8070 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
@@ -49,6 +49,7 @@
) : CameraController {
override val cameraId: CameraId
get() = graphConfig.camera
+ override var isForeground = false
private val lock = Any()
private var currentSurfaceMap: Map<StreamId, Surface> = emptyMap()
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 2271494..fc864da 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
@@ -39,6 +39,12 @@
val cameraId: CameraId
/**
+ * Whether the camera is being used in a foreground setting, and thus should be kept open on a
+ * best-effort basis, for example continuously retrying on a longer timeout.
+ */
+ var isForeground: Boolean
+
+ /**
* Connect and start the underlying camera. This may be called on the main thread and should not
* make long blocking calls. This may be called opportunistically (eg, whenever a lifecycle
* indicates the camera should be in a running state)
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 ee57224..e25f4f7 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
@@ -45,6 +45,13 @@
val graphState: StateFlow<GraphState>
/**
+ * This is a hint an app can give to a camera graph to indicate whether the camera is being used
+ * in a foreground setting, for example whether the user could see the app itself. This would
+ * inform the underlying implementation to open cameras more actively (e.g., longer timeout).
+ */
+ var isForeground: Boolean
+
+ /**
* This will cause the [CameraGraph] to start opening the [CameraDevice] and configuring a
* [CameraCaptureSession]. While the CameraGraph is alive it will attempt to keep the camera
* open, active, and in a configured running state.
@@ -209,6 +216,7 @@
enum class OperatingMode {
NORMAL,
HIGH_SPEED,
+ EXTENSION,
}
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ApiCompat.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ApiCompat.kt
index 66767ad..33c87c3 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ApiCompat.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ApiCompat.kt
@@ -21,10 +21,12 @@
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraExtensionSession
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CaptureRequest
import android.hardware.camera2.CaptureResult
import android.hardware.camera2.TotalCaptureResult
+import android.hardware.camera2.params.ExtensionSessionConfiguration
import android.hardware.camera2.params.InputConfiguration
import android.hardware.camera2.params.OutputConfiguration
import android.hardware.camera2.params.SessionConfiguration
@@ -290,6 +292,29 @@
}
}
+@RequiresApi(Build.VERSION_CODES.S)
+internal object Api31Compat {
+ @JvmStatic
+ @DoNotInline
+ fun createExtensionCaptureSession(
+ cameraDevice: CameraDevice,
+ extensionConfiguration: ExtensionSessionConfiguration
+ ) {
+ cameraDevice.createExtensionSession(extensionConfiguration)
+ }
+
+ @JvmStatic
+ @DoNotInline
+ fun newExtensionSessionConfiguration(
+ extensionMode: Int,
+ outputs: List<OutputConfiguration?>,
+ executor: Executor,
+ stateCallback: CameraExtensionSession.StateCallback
+ ): ExtensionSessionConfiguration {
+ return ExtensionSessionConfiguration(extensionMode, outputs, executor, stateCallback)
+ }
+}
+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
internal object Api33Compat {
@JvmStatic
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 3d4ea8d..73ce419 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
@@ -16,6 +16,7 @@
package androidx.camera.camera2.pipe.compat
+import android.os.Build
import android.view.Surface
import androidx.annotation.GuardedBy
import androidx.annotation.RequiresApi
@@ -64,6 +65,15 @@
private val lock = Any()
+ override var isForeground: Boolean
+ get() = synchronized(lock) { _isForeground }
+ set(value) = synchronized(lock) {
+ _isForeground = value
+ }
+
+ @GuardedBy("lock")
+ private var _isForeground: Boolean = false
+
@GuardedBy("lock")
private var controllerState: ControllerState = ControllerState.STOPPED
@@ -88,8 +98,8 @@
val camera = virtualCameraManager.open(
config.camera,
config.flags.allowMultipleActiveCameras,
- graphListener
- )
+ graphListener,
+ ) { _ -> isForeground }
check(currentCamera == null)
check(currentSession == null)
@@ -113,6 +123,7 @@
controllerState = ControllerState.STARTED
Log.debug { "Started Camera2CameraController" }
+ currentCameraStateJob?.cancel()
currentCameraStateJob = scope.launch { bindSessionToCamera() }
}
@@ -182,6 +193,9 @@
currentCamera = null
currentSession = null
+ currentCameraStateJob?.cancel()
+ currentCameraStateJob = null
+
scope.launch {
session?.disconnect()
camera?.disconnect()
@@ -240,6 +254,12 @@
) {
controllerState = ControllerState.DISCONNECTED
Log.debug { "Camera2CameraController is disconnected" }
+ if (Build.VERSION.SDK_INT in (Build.VERSION_CODES.Q..Build.VERSION_CODES.S_V2) &&
+ _isForeground
+ ) {
+ Log.debug { "Quirk for multi-resume: Internal tryRestart()" }
+ tryRestart(CameraStatus.CameraPrioritiesChanged)
+ }
} else {
controllerState = ControllerState.ERROR
Log.debug {
@@ -251,7 +271,5 @@
} else {
controllerState = ControllerState.STOPPED
}
- currentCameraStateJob?.cancel()
- currentCameraStateJob = null
}
}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2DeviceCloser.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2DeviceCloser.kt
index 5045610..0f34b05 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2DeviceCloser.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2DeviceCloser.kt
@@ -35,6 +35,7 @@
fun closeCamera(
cameraDeviceWrapper: CameraDeviceWrapper? = null,
cameraDevice: CameraDevice? = null,
+ closeUnderError: Boolean = false,
androidCameraState: AndroidCameraState,
)
}
@@ -48,6 +49,7 @@
override fun closeCamera(
cameraDeviceWrapper: CameraDeviceWrapper?,
cameraDevice: CameraDevice?,
+ closeUnderError: Boolean,
androidCameraState: AndroidCameraState,
) {
Log.debug { "Closing $cameraDeviceWrapper and/or $cameraDevice" }
@@ -59,22 +61,23 @@
"but the accompanied camera device has camera ID ${it.id}"
}
}
- closeCameraDevice(unwrappedCameraDevice, androidCameraState)
+ closeCameraDevice(unwrappedCameraDevice, closeUnderError, androidCameraState)
cameraDeviceWrapper.onDeviceClosed()
// We only need to close the device once (don't want to create another capture session).
// Return here.
return
}
- cameraDevice?.let { closeCameraDevice(it, androidCameraState) }
+ cameraDevice?.let { closeCameraDevice(it, closeUnderError, androidCameraState) }
}
private fun closeCameraDevice(
cameraDevice: CameraDevice,
+ closeUnderError: Boolean,
androidCameraState: AndroidCameraState,
) {
val cameraId = CameraId.fromCamera2Id(cameraDevice.id)
- if (camera2Quirks.shouldCreateCaptureSessionBeforeClosing(cameraId)) {
+ if (camera2Quirks.shouldCreateCaptureSessionBeforeClosing(cameraId) && !closeUnderError) {
Debug.trace("Camera2DeviceCloserImpl#createCaptureSession") {
Log.debug { "Creating an empty capture session before closing camera $cameraId" }
createCaptureSession(cameraDevice)
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraDeviceWrapper.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraDeviceWrapper.kt
index 521e474..8981549 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraDeviceWrapper.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraDeviceWrapper.kt
@@ -18,8 +18,9 @@
package androidx.camera.camera2.pipe.compat
-import android.hardware.camera2.CameraCaptureSession
+import android.hardware.camera2.CameraCaptureSession.StateCallback
import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraExtensionSession
import android.hardware.camera2.CaptureRequest
import android.hardware.camera2.TotalCaptureResult
import android.hardware.camera2.params.InputConfiguration
@@ -105,6 +106,10 @@
@RequiresApi(Build.VERSION_CODES.P)
fun createCaptureSession(config: SessionConfigData): Boolean
+ /** @see CameraDevice.createExtensionSession */
+ @RequiresApi(Build.VERSION_CODES.S)
+ fun createExtensionSession(config: SessionConfigData): Boolean
+
/** Invoked when the [CameraDevice] has been closed */
fun onDeviceClosed()
}
@@ -126,9 +131,10 @@
private val cameraDevice: CameraDevice,
override val cameraId: CameraId,
private val cameraErrorListener: CameraErrorListener,
- private val interopSessionStateCallback: CameraCaptureSession.StateCallback? = null
+ private val interopSessionStateCallback: StateCallback? = null,
+ private val interopExtensionSessionStateCallback: CameraExtensionSession.StateCallback? = null,
) : CameraDeviceWrapper {
- private val _lastStateCallback = atomic<CameraCaptureSessionWrapper.StateCallback?>(null)
+ private val _lastStateCallback = atomic<OnSessionFinalized?>(null)
override fun createCaptureSession(
outputs: List<Surface>,
@@ -154,6 +160,35 @@
)
} != null
+ @RequiresApi(Build.VERSION_CODES.S)
+ override fun createExtensionSession(config: SessionConfigData): Boolean =
+ catchAndReportCameraExceptions(cameraId, cameraErrorListener) {
+ checkNotNull(config.extensionStateCallback) {
+ "extensionStateCallback must be set to create Extension session"
+ }
+ checkNotNull(config.extensionMode) {
+ "extensionMode must be set to create Extension session"
+ }
+ val stateCallback = config.extensionStateCallback
+ val previousStateCallback = _lastStateCallback.getAndSet(stateCallback)
+ val sessionConfig =
+ Api31Compat.newExtensionSessionConfiguration(
+ config.extensionMode,
+ config.outputConfigurations.map {
+ it.unwrapAs(OutputConfiguration::class)
+ },
+ config.executor,
+ AndroidExtensionSessionStateCallback(
+ this,
+ stateCallback,
+ previousStateCallback,
+ cameraErrorListener,
+ interopExtensionSessionStateCallback
+ ),
+ )
+ Api31Compat.createExtensionCaptureSession(cameraDevice, sessionConfig)
+ } != null
+
@RequiresApi(23)
override fun createReprocessableCaptureSession(
input: InputConfiguration,
@@ -451,6 +486,17 @@
}
}
+ @RequiresApi(31)
+ override fun createExtensionSession(config: SessionConfigData) = synchronized(lock) {
+ if (disconnected) {
+ Log.warn { "createExtensionSession failed: Virtual device disconnected" }
+ config.extensionStateCallback!!.onSessionFinalized()
+ false
+ } else {
+ androidCameraDevice.createExtensionSession(config)
+ }
+ }
+
@RequiresApi(28)
override fun createCaptureSession(config: SessionConfigData) = synchronized(lock) {
if (disconnected) {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactory.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactory.kt
index 254be23..be00cd3 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactory.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactory.kt
@@ -60,8 +60,16 @@
androidMHighSpeedProvider: Provider<AndroidMHighSpeedSessionFactory>,
androidNProvider: Provider<AndroidNSessionFactory>,
androidPProvider: Provider<AndroidPSessionFactory>,
+ androidExtensionProvider: Provider<AndroidExtensionSessionFactory>,
graphConfig: CameraGraph.Config
): CaptureSessionFactory {
+ if (graphConfig.sessionMode == CameraGraph.OperatingMode.EXTENSION) {
+ check(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ "Cannot use Extension sessions below Android S"
+ }
+ return androidExtensionProvider.get()
+ }
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return androidPProvider.get()
}
@@ -248,6 +256,8 @@
when (graphConfig.sessionMode) {
CameraGraph.OperatingMode.NORMAL -> SessionConfigData.SESSION_TYPE_REGULAR
CameraGraph.OperatingMode.HIGH_SPEED -> SessionConfigData.SESSION_TYPE_HIGH_SPEED
+ else -> throw IllegalArgumentException(
+ "Unsupported session mode: ${graphConfig.sessionMode}")
}
val outputs = buildOutputConfigurations(
@@ -389,6 +399,65 @@
return OutputConfigurations(allOutputs, deferredOutputs)
}
+@RequiresApi(Build.VERSION_CODES.S)
+internal class AndroidExtensionSessionFactory
+@Inject
+constructor(
+ private val threads: Threads,
+ private val graphConfig: CameraGraph.Config,
+ private val streamGraph: StreamGraphImpl,
+ private val camera2MetadataProvider: Camera2MetadataProvider
+) : CaptureSessionFactory {
+ override fun create(
+ cameraDevice: CameraDeviceWrapper,
+ surfaces: Map<StreamId, Surface>,
+ captureSessionState: CaptureSessionState,
+ ): Map<StreamId, OutputConfigurationWrapper> {
+ val operatingMode =
+ when (graphConfig.sessionMode) {
+ CameraGraph.OperatingMode.EXTENSION -> SessionConfigData.SESSION_TYPE_EXTENSION
+ else -> throw IllegalArgumentException(
+ "Unsupported session mode: ${graphConfig.sessionMode}")
+ }
+
+ val outputs = buildOutputConfigurations(
+ graphConfig,
+ streamGraph,
+ surfaces,
+ camera2MetadataProvider,
+ cameraDevice.cameraId
+ )
+ if (outputs.all.isEmpty()) {
+ Log.warn { "Failed to create OutputConfigurations for $graphConfig" }
+ return emptyMap()
+ }
+
+ check(graphConfig.input == null) { "Reprocessing is not supported for Extensions" }
+
+ // TODO(b/276971147): get extensionMode from metadata and create extensionCaptureCallback
+ val sessionConfig =
+ SessionConfigData(
+ operatingMode,
+ graphConfig.input,
+ outputs.all,
+ threads.camera2Executor,
+ captureSessionState,
+ graphConfig.sessionTemplate.value,
+ graphConfig.sessionParameters
+ )
+
+ if (!cameraDevice.createExtensionSession(sessionConfig)) {
+ Log.warn {
+ "Failed to create ExtensionCaptureSession from $cameraDevice " +
+ "for $captureSessionState!"
+ }
+ captureSessionState.disconnect()
+ }
+
+ return emptyMap()
+ }
+}
+
internal data class OutputConfigurations(
val all: List<OutputConfigurationWrapper>,
val deferred: Map<StreamId, OutputConfigurationWrapper>
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionWrapper.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionWrapper.kt
index 1b2637b..47b388f 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionWrapper.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionWrapper.kt
@@ -128,7 +128,7 @@
fun finalizeOutputConfigurations(outputConfigs: List<OutputConfigurationWrapper>): Boolean
/** @see CameraCaptureSession.StateCallback */
- interface StateCallback {
+ interface StateCallback : OnSessionFinalized {
/** @see CameraCaptureSession.StateCallback.onActive */
fun onActive(session: CameraCaptureSessionWrapper)
@@ -146,16 +146,6 @@
/** @see CameraCaptureSession.StateCallback.onReady */
fun onCaptureQueueEmpty(session: CameraCaptureSessionWrapper)
-
- /**
- * Artificial event indicating the session is no longer in use and may be called several
- * times. [onClosed] and [onConfigureFailed] will call this method directly. This method
- * should also be called whenever the underlying camera devices is closed, and whenever a
- * subsequent capture session is configured on the same camera device.
- *
- * See b/249258992 for more details.
- */
- fun onSessionFinalized()
}
}
@@ -174,7 +164,7 @@
internal class AndroidCaptureSessionStateCallback(
private val device: CameraDeviceWrapper,
private val stateCallback: CameraCaptureSessionWrapper.StateCallback,
- lastStateCallback: CameraCaptureSessionWrapper.StateCallback?,
+ lastStateCallback: OnSessionFinalized?,
private val cameraErrorListener: CameraErrorListener,
private val interopSessionStateCallback: CameraCaptureSession.StateCallback? = null
) : CameraCaptureSession.StateCallback() {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Configuration.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Configuration.kt
index d5b60d1..042b6ca 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Configuration.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Configuration.kt
@@ -52,12 +52,15 @@
val executor: Executor,
val stateCallback: CameraCaptureSessionWrapper.StateCallback,
val sessionTemplateId: Int,
- val sessionParameters: Map<*, Any?>
+ val sessionParameters: Map<*, Any?>,
+ val extensionMode: Int? = null,
+ val extensionStateCallback: CameraExtensionSessionWrapper.StateCallback? = null
) {
companion object {
/* NOTE: These must keep in sync with their SessionConfiguration values. */
const val SESSION_TYPE_REGULAR = 0
const val SESSION_TYPE_HIGH_SPEED = 1
+ const val SESSION_TYPE_EXTENSION = 2
}
}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionWrapper.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionWrapper.kt
new file mode 100644
index 0000000..e75ce9a
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionWrapper.kt
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RequiresApi(31) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+
+package androidx.camera.camera2.pipe.compat
+
+import android.hardware.camera2.CameraExtensionSession
+import android.hardware.camera2.CaptureRequest
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.UnsafeWrapper
+import androidx.camera.camera2.pipe.internal.CameraErrorListener
+import java.util.concurrent.Executor
+import kotlin.reflect.KClass
+import kotlinx.atomicfu.atomic
+
+/**
+ * Interface shim for [CameraExtensionSession] with minor modifications.
+ *
+ * This interface has been modified to correct nullness, adjust exceptions, and to return or produce
+ * wrapper interfaces instead of the native Camera2 types.
+ */
+internal interface CameraExtensionSessionWrapper : UnsafeWrapper, AutoCloseable {
+
+ /**
+ * @return The [CameraDeviceWrapper] that created this CameraExtensionSession
+ * @see [CameraExtensionSession.getDevice]
+ */
+ val device: CameraDeviceWrapper
+
+ /**
+ * @param request The settings for this exposure
+ * @param listener The callback object to notify once this request has been processed.
+ * @param executor The executor on which the listener should be invoked, or null to use the
+ * current thread's looper.
+ * @return An unique capture sequence id.
+ * @see [CameraExtensionSession.capture].
+ */
+ fun capture(
+ request: CaptureRequest,
+ executor: Executor,
+ listener: CameraExtensionSession.ExtensionCaptureCallback
+ ): Int?
+
+ /**
+ * @param request The request to repeat indefinitely.
+ * @param executor The executor on which the listener should be invoked, or null to use the
+ * current thread's looper.
+ * @param listener The callback object to notify every time the request finishes processing.
+ * @return An unique capture sequence ID.
+ * @see [CameraExtensionSession.setRepeatingRequest].
+ */
+ fun setRepeatingRequest(
+ request: CaptureRequest,
+ executor: Executor,
+ listener: CameraExtensionSession.ExtensionCaptureCallback
+ ): Int?
+
+ /** @see [CameraExtensionSession.stopRepeating]. */
+ fun stopRepeating(): Boolean
+
+ /** @see CameraExtensionSession.StateCallback */
+ interface StateCallback : OnSessionFinalized {
+ /** @see CameraExtensionSession.StateCallback.onClosed */
+ fun onClosed(session: CameraExtensionSessionWrapper)
+
+ /** @see CameraExtensionSession.StateCallback.onConfigureFailed */
+ fun onConfigureFailed(session: CameraExtensionSessionWrapper)
+
+ /** @see CameraExtensionSession.StateCallback.onConfigured */
+ fun onConfigured(session: CameraExtensionSessionWrapper)
+ }
+}
+
+@RequiresApi(31) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+internal class AndroidExtensionSessionStateCallback(
+ private val device: CameraDeviceWrapper,
+ private val stateCallback: CameraExtensionSessionWrapper.StateCallback,
+ lastStateCallback: OnSessionFinalized?,
+ private val cameraErrorListener: CameraErrorListener,
+ private val interopSessionStateCallback: CameraExtensionSession.StateCallback? = null
+) : CameraExtensionSession.StateCallback() {
+ private val _lastStateCallback = atomic(lastStateCallback)
+ private val extensionSession = atomic<CameraExtensionSessionWrapper?>(null)
+
+ override fun onConfigured(session: CameraExtensionSession) {
+ stateCallback.onConfigured(getWrapped(session, cameraErrorListener))
+
+ // b/249258992 - This is a workaround to ensure previous
+ // CameraExtensionSession.StateCallback instances receive some kind of "finalization"
+ // signal if onClosed is not fired by the framework after a subsequent session
+ // has been configured.
+ finalizeLastSession()
+ interopSessionStateCallback?.onConfigured(session)
+ }
+
+ override fun onConfigureFailed(session: CameraExtensionSession) {
+ stateCallback.onConfigureFailed(getWrapped(session, cameraErrorListener))
+ finalizeSession()
+ interopSessionStateCallback?.onConfigureFailed(session)
+ }
+
+ override fun onClosed(session: CameraExtensionSession) {
+ stateCallback.onClosed(getWrapped(session, cameraErrorListener))
+ finalizeSession()
+ interopSessionStateCallback?.onClosed(session)
+ }
+
+ private fun getWrapped(
+ session: CameraExtensionSession,
+ cameraErrorListener: CameraErrorListener,
+ ): CameraExtensionSessionWrapper {
+ var local = extensionSession.value
+ if (local != null) {
+ return local
+ }
+
+ local = wrapSession(session, cameraErrorListener)
+ if (extensionSession.compareAndSet(null, local)) {
+ return local
+ }
+ return extensionSession.value!!
+ }
+
+ private fun wrapSession(
+ session: CameraExtensionSession,
+ cameraErrorListener: CameraErrorListener,
+ ): CameraExtensionSessionWrapper {
+ return AndroidCameraExtensionSession(device, session, cameraErrorListener)
+ }
+
+ private fun finalizeSession() {
+ finalizeLastSession()
+ stateCallback.onSessionFinalized()
+ }
+
+ private fun finalizeLastSession() {
+ // Clear out the reference to the previous session, if one was set.
+ val previousSession = _lastStateCallback.getAndSet(null)
+ previousSession?.let { previousSession.onSessionFinalized() }
+ }
+}
+
+@RequiresApi(31) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+internal open class AndroidCameraExtensionSession(
+ override val device: CameraDeviceWrapper,
+ private val cameraExtensionSession: CameraExtensionSession,
+ private val cameraErrorListener: CameraErrorListener,
+) : CameraExtensionSessionWrapper {
+ override fun capture(
+ request: CaptureRequest,
+ executor: Executor,
+ listener: CameraExtensionSession.ExtensionCaptureCallback
+ ): Int? = catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
+ cameraExtensionSession.capture(
+ request,
+ executor,
+ listener,
+ )
+ }
+
+ override fun setRepeatingRequest(
+ request: CaptureRequest,
+ executor: Executor,
+ listener: CameraExtensionSession.ExtensionCaptureCallback,
+ ): Int? = catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
+ cameraExtensionSession.setRepeatingRequest(request, executor, listener)
+ }
+
+ override fun stopRepeating(): Boolean =
+ catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
+ cameraExtensionSession.stopRepeating()
+ } != null
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : Any> unwrapAs(type: KClass<T>): T? =
+ when (type) {
+ CameraExtensionSession::class -> cameraExtensionSession as T?
+ else -> null
+ }
+
+ override fun close() {
+ return cameraExtensionSession.close()
+ }
+}
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 f8cfa64..396ace8 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
@@ -53,6 +53,7 @@
override val cameraId: CameraId
get() = graphConfig.camera
+ override var isForeground = false
override fun start() {
if (started.compareAndSet(expect = false, update = true)) {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/OnSessionFinalized.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/OnSessionFinalized.kt
new file mode 100644
index 0000000..67aa9a5
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/OnSessionFinalized.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.compat
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+
+/**
+ * Base class for CameraCaptureSession.StateCallback()
+ */
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+interface OnSessionFinalized {
+ /**
+ * Artificial event indicating the session is no longer in use and may be called several
+ * times. onClosed() and [onConfigureFailed() methods should call this method directly.
+ * This method should also be called whenever the underlying camera devices is closed, and
+ * whenever a subsequent capture session is configured on the same camera device.
+ *
+ * See b/249258992 for more details.
+ */
+ fun onSessionFinalized()
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt
index e5929d6..a5d46ea 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt
@@ -28,7 +28,6 @@
import androidx.camera.camera2.pipe.core.Debug
import androidx.camera.camera2.pipe.core.DurationNs
import androidx.camera.camera2.pipe.core.Log
-import androidx.camera.camera2.pipe.core.SystemTimeSource
import androidx.camera.camera2.pipe.core.Threads
import androidx.camera.camera2.pipe.core.TimeSource
import androidx.camera.camera2.pipe.core.TimestampNs
@@ -43,8 +42,22 @@
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
+// TODO(b/246180670): Replace all duration usage in CameraPipe with kotlin.time.Duration
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
-private val cameraRetryTimeout = DurationNs(10_000_000_000) // 10 seconds
+private val defaultCameraRetryTimeoutNs = DurationNs(10_000_000_000L) // 10s
+
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+private val activeResumeCameraRetryTimeoutNs = DurationNs(30L * 60L * 1_000_000_000L) // 30m
+
+private const val defaultCameraRetryDelayMs = 500L
+
+private const val activeResumeCameraRetryDelayBaseMs = defaultCameraRetryDelayMs
+
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+private val activeResumeCameraRetryThresholds = arrayOf(
+ DurationNs(2L * 60L * 1_000_000_000L), // 2m
+ DurationNs(5L * 60L * 1_000_000_000L), // 5m
+)
internal interface CameraOpener {
fun openCamera(cameraId: CameraId, stateCallback: StateCallback)
@@ -216,6 +229,7 @@
) {
internal suspend fun openCameraWithRetry(
cameraId: CameraId,
+ isForegroundObserver: (Unit) -> Boolean = { _ -> true },
): OpenCameraResult {
val requestTimestamp = Timestamps.now(timeSource)
var attempts = 0
@@ -229,6 +243,7 @@
attempts,
requestTimestamp,
)
+ val elapsed = Timestamps.now(timeSource) - requestTimestamp
with(result) {
if (cameraState != null) {
return result
@@ -246,13 +261,14 @@
return result
}
+ val isForeground = isForegroundObserver.invoke(Unit)
val willRetry =
shouldRetry(
errorCode,
attempts,
- requestTimestamp,
- timeSource,
- devicePolicyManager.camerasDisabled
+ elapsed,
+ devicePolicyManager.camerasDisabled,
+ isForeground,
)
// Always notify if the decision is to not retry the camera open, otherwise allow
// 1 open call to happen silently without generating an error, and notify about each
@@ -268,12 +284,19 @@
}
return result
}
- }
- // Listen to availability - if we are notified that the cameraId is available then
- // retry immediately.
- if (!cameraAvailabilityMonitor.awaitAvailableCamera(cameraId, timeoutMillis = 500)) {
- Log.debug { "Timeout expired, retrying camera open for camera $cameraId" }
+ // Listen to availability - if we are notified that the cameraId is available then
+ // retry immediately.
+ if (!cameraAvailabilityMonitor.awaitAvailableCamera(
+ cameraId,
+ timeoutMillis = getRetryDelayMs(
+ elapsed,
+ shouldActivateActiveResume(isForeground, errorCode)
+ )
+ )
+ ) {
+ Log.debug { "Timeout expired, retrying camera open for camera $cameraId" }
+ }
}
}
}
@@ -282,12 +305,13 @@
internal fun shouldRetry(
errorCode: CameraError,
attempts: Int,
- firstAttemptTimestampNs: TimestampNs,
- timeSource: TimeSource = SystemTimeSource(),
- camerasDisabledByDevicePolicy: Boolean
+ elapsedNs: DurationNs,
+ camerasDisabledByDevicePolicy: Boolean,
+ isForeground: Boolean = false,
): Boolean {
- val elapsed = Timestamps.now(timeSource) - firstAttemptTimestampNs
- if (elapsed > cameraRetryTimeout) {
+ val shouldActiveResume = shouldActivateActiveResume(isForeground, errorCode)
+ if (shouldActiveResume) Log.debug { "shouldRetry: Active resume mode is activated" }
+ if (elapsedNs > getRetryTimeoutNs(shouldActiveResume)) {
return false
}
return when (errorCode) {
@@ -350,5 +374,34 @@
}
}
}
+
+ internal fun shouldActivateActiveResume(
+ isForeground: Boolean,
+ errorCode: CameraError
+ ): Boolean = isForeground &&
+ Build.VERSION.SDK_INT in (Build.VERSION_CODES.Q..Build.VERSION_CODES.S_V2) &&
+ (errorCode == CameraError.ERROR_CAMERA_IN_USE ||
+ errorCode == CameraError.ERROR_CAMERA_LIMIT_EXCEEDED ||
+ errorCode == CameraError.ERROR_CAMERA_DISCONNECTED)
+
+ internal fun getRetryTimeoutNs(activeResumeActivated: Boolean) =
+ if (!activeResumeActivated) {
+ defaultCameraRetryTimeoutNs
+ } else {
+ activeResumeCameraRetryTimeoutNs
+ }
+
+ internal fun getRetryDelayMs(elapsedNs: DurationNs, activeResumeActivated: Boolean): Long {
+ if (!activeResumeActivated) {
+ return defaultCameraRetryDelayMs
+ }
+ return if (elapsedNs < activeResumeCameraRetryThresholds[0]) {
+ activeResumeCameraRetryDelayBaseMs
+ } else if (elapsedNs < activeResumeCameraRetryThresholds[1]) {
+ activeResumeCameraRetryDelayBaseMs * 4L
+ } else {
+ activeResumeCameraRetryDelayBaseMs * 8L
+ }
+ }
}
}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCamera.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCamera.kt
index 9fa8135..25380b7 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCamera.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCamera.kt
@@ -19,8 +19,9 @@
package androidx.camera.camera2.pipe.compat
-import android.hardware.camera2.CameraCaptureSession
+import android.hardware.camera2.CameraCaptureSession.StateCallback
import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraExtensionSession
import androidx.annotation.GuardedBy
import androidx.annotation.RequiresApi
import androidx.camera.camera2.pipe.CameraError
@@ -251,7 +252,8 @@
private val cameraErrorListener: CameraErrorListener,
private val camera2DeviceCloser: Camera2DeviceCloser,
private val interopDeviceStateCallback: CameraDevice.StateCallback? = null,
- private val interopSessionStateCallback: CameraCaptureSession.StateCallback? = null
+ private val interopSessionStateCallback: StateCallback? = null,
+ private val interopExtensionSessionStateCallback: CameraExtensionSession.StateCallback? = null
) : CameraDevice.StateCallback() {
private val debugId = androidCameraDebugIds.incrementAndGet()
private val lock = Any()
@@ -322,17 +324,19 @@
// This checks to see if close() has been invoked, or one of the close methods have been
// invoked. If so, call close() on the cameraDevice outside of the synchronized block.
- var closeCamera = false
- synchronized(lock) {
- if (pendingClose != null) {
- closeCamera = true
- } else {
+ val currentCloseInfo = synchronized(lock) {
+ if (pendingClose == null) {
opening = true
}
+ pendingClose
}
interopDeviceStateCallback?.onOpened(cameraDevice)
- if (closeCamera) {
- camera2DeviceCloser.closeCamera(cameraDevice = cameraDevice, androidCameraState = this)
+ if (currentCloseInfo != null) {
+ camera2DeviceCloser.closeCamera(
+ cameraDevice = cameraDevice,
+ closeUnderError = currentCloseInfo.errorCode != null,
+ androidCameraState = this
+ )
return
}
@@ -345,7 +349,8 @@
cameraDevice,
cameraId,
cameraErrorListener,
- interopSessionStateCallback
+ interopSessionStateCallback,
+ interopExtensionSessionStateCallback
)
)
@@ -357,7 +362,11 @@
}
if (closeInfo != null) {
_state.value = CameraStateClosing(closeInfo.errorCode)
- camera2DeviceCloser.closeCamera(cameraDevice = cameraDevice, androidCameraState = this)
+ camera2DeviceCloser.closeCamera(
+ cameraDevice = cameraDevice,
+ closeUnderError = closeInfo.errorCode != null,
+ androidCameraState = this
+ )
_state.value = computeClosedState(closeInfo)
}
Debug.traceStop()
@@ -463,6 +472,7 @@
camera2DeviceCloser.closeCamera(
cameraDeviceWrapper,
cameraDevice,
+ closeUnderError = closeInfo.errorCode != null,
androidCameraState = this,
)
_state.value = computeClosedState(closeInfo)
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCameraManager.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCameraManager.kt
index 8194f28..c4906ae 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCameraManager.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCameraManager.kt
@@ -42,7 +42,8 @@
internal data class RequestOpen(
val virtualCamera: VirtualCameraState,
val share: Boolean = false,
- val graphListener: GraphListener
+ val graphListener: GraphListener,
+ val isForegroundObserver: (Unit) -> Boolean,
) : CameraRequest()
internal data class RequestClose(val activeCamera: VirtualCameraManager.ActiveCamera) :
@@ -77,10 +78,11 @@
internal fun open(
cameraId: CameraId,
share: Boolean = false,
- graphListener: GraphListener
+ graphListener: GraphListener,
+ isForegroundObserver: (Unit) -> Boolean,
): VirtualCamera {
val result = VirtualCameraState(cameraId, graphListener)
- offerChecked(RequestOpen(result, share, graphListener))
+ offerChecked(RequestOpen(result, share, graphListener, isForegroundObserver))
return result
}
@@ -186,7 +188,11 @@
var realCamera = activeCameras.firstOrNull { it.cameraId == cameraIdToOpen }
if (realCamera == null) {
val openResult =
- openCameraWithRetry(cameraIdToOpen, scope = this)
+ openCameraWithRetry(
+ cameraIdToOpen,
+ request.isForegroundObserver,
+ scope = this
+ )
if (openResult.activeCamera != null) {
realCamera = openResult.activeCamera
activeCameras.add(realCamera)
@@ -205,6 +211,7 @@
private suspend fun openCameraWithRetry(
cameraId: CameraId,
+ isForegroundObserver: (Unit) -> Boolean,
scope: CoroutineScope
): OpenVirtualCameraResult {
// TODO: Figure out how 1-time permissions work, and see if they can be reset without
@@ -212,7 +219,7 @@
check(permissions.hasCameraPermission) { "Missing camera permissions!" }
Log.debug { "Opening $cameraId with retries..." }
- val result = retryingCameraStateOpener.openCameraWithRetry(cameraId)
+ val result = retryingCameraStateOpener.openCameraWithRetry(cameraId, isForegroundObserver)
if (result.cameraState == null) {
return OpenVirtualCameraResult(lastCameraError = result.errorCode)
}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Debug.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Debug.kt
index 871c47a..70c0f57 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Debug.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Debug.kt
@@ -106,6 +106,7 @@
when (graphConfig.sessionMode) {
CameraGraph.OperatingMode.HIGH_SPEED -> "High Speed"
CameraGraph.OperatingMode.NORMAL -> "Normal"
+ CameraGraph.OperatingMode.EXTENSION -> "Extension"
}
val capabilities = metadata[REQUEST_AVAILABLE_CAPABILITIES]
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 6947f9c..3922788 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
@@ -104,6 +104,14 @@
override val graphState: StateFlow<GraphState>
get() = graphProcessor.graphState
+ private var _isForeground = false
+ override var isForeground: Boolean
+ get() = _isForeground
+ set(value) {
+ _isForeground = value
+ cameraController.isForeground = value
+ }
+
override fun start() {
Debug.traceStart { "$this#start" }
Log.info { "Starting $this" }
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt
index a714105..c6f20ef 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt
@@ -24,13 +24,13 @@
import androidx.camera.camera2.pipe.CameraError.Companion.ERROR_CAMERA_DISCONNECTED
import androidx.camera.camera2.pipe.CameraError.Companion.ERROR_CAMERA_IN_USE
import androidx.camera.camera2.pipe.CameraError.Companion.ERROR_CAMERA_LIMIT_EXCEEDED
+import androidx.camera.camera2.pipe.CameraError.Companion.ERROR_CAMERA_SERVICE
import androidx.camera.camera2.pipe.CameraError.Companion.ERROR_DO_NOT_DISTURB_ENABLED
import androidx.camera.camera2.pipe.CameraError.Companion.ERROR_ILLEGAL_ARGUMENT_EXCEPTION
import androidx.camera.camera2.pipe.CameraError.Companion.ERROR_SECURITY_EXCEPTION
import androidx.camera.camera2.pipe.CameraId
import androidx.camera.camera2.pipe.CameraMetadata
import androidx.camera.camera2.pipe.core.DurationNs
-import androidx.camera.camera2.pipe.core.TimestampNs
import androidx.camera.camera2.pipe.core.Timestamps
import androidx.camera.camera2.pipe.internal.CameraErrorListener
import androidx.camera.camera2.pipe.testing.FakeCamera2DeviceCloser
@@ -128,75 +128,63 @@
@Test
fun testShouldRetryReturnsTrueWithinTimeout() {
- val firstAttemptTimestamp = TimestampNs(0L)
- fakeTimeSource.currentTimestamp = TimestampNs(1_000_000_000L) // 1 second
-
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_CAMERA_IN_USE,
1,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_000L), // 1 second
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
}
@Test
fun testShouldRetryReturnsFalseWhenTimeoutExpires() {
- val firstAttemptTimestamp = TimestampNs(0L)
- fakeTimeSource.currentTimestamp = TimestampNs(30_000_000_000L) // 30 seconds
-
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_CAMERA_IN_USE,
1,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(30_000_000_000L), // 30 seconds
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isFalse()
+ ).isFalse()
}
@Test
fun testShouldRetryShouldFailUndetermined() {
- val firstAttemptTimestamp = TimestampNs(0L)
- fakeTimeSource.currentTimestamp = TimestampNs(1_000_000_000L) // 1 second
-
assertThat(
RetryingCameraStateOpener.shouldRetry(
CameraError.ERROR_UNDETERMINED,
1,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_000L), // 1 second
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isFalse()
+ ).isFalse()
}
@Test
fun testShouldRetryCameraInUse() {
- val firstAttemptTimestamp = TimestampNs(0L)
- fakeTimeSource.currentTimestamp = TimestampNs(1_000_000_000L) // 1 second
-
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_CAMERA_IN_USE,
1,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_000L), // 1 second
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
// The second retry attempt should fail if SDK version < S, and succeed otherwise.
val secondRetry =
RetryingCameraStateOpener.shouldRetry(
- ERROR_CAMERA_IN_USE, 2, firstAttemptTimestamp, fakeTimeSource, false
+ ERROR_CAMERA_IN_USE,
+ 2,
+ DurationNs(1_000_000_001L),
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
assertThat(secondRetry).isFalse()
@@ -207,251 +195,343 @@
@Test
fun testShouldRetryCameraLimitExceeded() {
- val firstAttemptTimestamp = TimestampNs(0L)
- fakeTimeSource.currentTimestamp = TimestampNs(1_000_000_000L) // 1 second
-
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_CAMERA_LIMIT_EXCEEDED,
1,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_000L), // 1 second
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
// Second attempt should succeed as well.
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_CAMERA_LIMIT_EXCEEDED,
2,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_001L),
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
}
@Test
fun testShouldRetryOnceCameraDisabledWhenDpcCameraDisabled() {
- val firstAttemptTimestamp = TimestampNs(0L)
- fakeTimeSource.currentTimestamp = TimestampNs(1_000_000_000L) // 1 second
-
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_CAMERA_DISABLED,
1,
- firstAttemptTimestamp,
- fakeTimeSource,
- camerasDisabledByDevicePolicy = true
+ DurationNs(1_000_000_000L), // 1 second
+ camerasDisabledByDevicePolicy = true,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
// Second attempt should fail if camera is disabled.
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_CAMERA_DISABLED,
2,
- firstAttemptTimestamp,
- fakeTimeSource,
- camerasDisabledByDevicePolicy = true
+ DurationNs(1_000_000_001L),
+ camerasDisabledByDevicePolicy = true,
+ isForeground = false,
)
- )
- .isFalse()
+ ).isFalse()
}
@Test
fun testShouldRetryRepeatedlyCameraDisabledWhenDpcCameraEnabled() {
- val firstAttemptTimestamp = TimestampNs(0L)
- fakeTimeSource.currentTimestamp = TimestampNs(1_000_000_000L) // 1 second
-
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_CAMERA_DISABLED,
1,
- firstAttemptTimestamp,
- fakeTimeSource,
- camerasDisabledByDevicePolicy = false
+ DurationNs(1_000_000_000L), // 1 second
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
// Second attempt should success if camera is not disabled.
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_CAMERA_DISABLED,
2,
- firstAttemptTimestamp,
- fakeTimeSource,
- camerasDisabledByDevicePolicy = false
+ DurationNs(1_000_000_001L),
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
}
@Test
fun testShouldRetryCameraDevice() {
- val firstAttemptTimestamp = TimestampNs(0L)
- fakeTimeSource.currentTimestamp = TimestampNs(1_000_000_000L) // 1 second
-
assertThat(
RetryingCameraStateOpener.shouldRetry(
CameraError.ERROR_CAMERA_DEVICE,
1,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_000L), // 1 second
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
// Second attempt should succeed as well.
assertThat(
RetryingCameraStateOpener.shouldRetry(
CameraError.ERROR_CAMERA_DEVICE,
2,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_001L),
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
}
@Test
fun testShouldRetryCameraService() {
- val firstAttemptTimestamp = TimestampNs(0L)
- fakeTimeSource.currentTimestamp = TimestampNs(1_000_000_000L) // 1 second
-
assertThat(
RetryingCameraStateOpener.shouldRetry(
- CameraError.ERROR_CAMERA_SERVICE,
+ ERROR_CAMERA_SERVICE,
1,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_000L), // 1 second
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
// Second attempt should succeed as well.
assertThat(
RetryingCameraStateOpener.shouldRetry(
- CameraError.ERROR_CAMERA_SERVICE,
+ ERROR_CAMERA_SERVICE,
2,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_001L),
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
}
@Test
fun testShouldRetryCameraDisconnected() {
- val firstAttemptTimestamp = TimestampNs(0L)
- fakeTimeSource.currentTimestamp = TimestampNs(1_000_000_000L) // 1 second
-
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_CAMERA_DISCONNECTED,
1,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_000L), // 1 second
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
// Second attempt should succeed as well.
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_CAMERA_DISCONNECTED,
2,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_001L),
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
}
@Test
fun testShouldRetryIllegalArgumentException() {
- val firstAttemptTimestamp = TimestampNs(0L)
- fakeTimeSource.currentTimestamp = TimestampNs(1_000_000_000L) // 1 second
-
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_ILLEGAL_ARGUMENT_EXCEPTION,
1,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_000L), // 1 second
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
// Second attempt should succeed as well.
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_ILLEGAL_ARGUMENT_EXCEPTION,
2,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_001L),
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
}
@Test
fun testShouldRetrySecurityException() {
- val firstAttemptTimestamp = TimestampNs(0L)
- fakeTimeSource.currentTimestamp = TimestampNs(1_000_000_000L) // 1 second
-
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_SECURITY_EXCEPTION,
1,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_000L), // 1 second
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isTrue()
+ ).isTrue()
// Second attempt should fail.
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_SECURITY_EXCEPTION,
2,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_001L),
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
- )
- .isFalse()
+ ).isFalse()
}
@Test
fun testShouldNotRetryDoNotDisturbModeEnabled() {
- val firstAttemptTimestamp = TimestampNs(0L)
- fakeTimeSource.currentTimestamp = TimestampNs(1_000_000_000L) // 1 second
-
assertThat(
RetryingCameraStateOpener.shouldRetry(
ERROR_DO_NOT_DISTURB_ENABLED,
1,
- firstAttemptTimestamp,
- fakeTimeSource,
- false
+ DurationNs(1_000_000_000L), // 1 second
+ camerasDisabledByDevicePolicy = false,
+ isForeground = false,
)
+ ).isFalse()
+ }
+
+ @Test
+ fun testShouldRetryCameraInUseOnLongerTimeoutWhenActiveResume() {
+ if (Build.VERSION.SDK_INT !in (Build.VERSION_CODES.Q..Build.VERSION_CODES.S_V2)) {
+ // We won't activate active resume mode when the API level is not in [Q, S_V2].
+ return
+ }
+ assertThat(
+ RetryingCameraStateOpener.shouldRetry(
+ ERROR_CAMERA_IN_USE,
+ 1,
+ DurationNs(1_000_000_000L),
+ camerasDisabledByDevicePolicy = false,
+ isForeground = true,
+ )
+ ).isTrue()
+
+ val secondRetry = RetryingCameraStateOpener.shouldRetry(
+ ERROR_CAMERA_IN_USE,
+ 2,
+ DurationNs(30_000_000_000L), // 30s
+ camerasDisabledByDevicePolicy = false,
+ isForeground = true,
)
- .isFalse()
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { // Multi-window available
+ assertThat(secondRetry).isFalse()
+ } else {
+ assertThat(secondRetry).isTrue()
+ }
+ }
+
+ @Test
+ fun testShouldRetryCameraLimitExceededOnLongerTimeoutWhenActiveResume() {
+ if (Build.VERSION.SDK_INT !in (Build.VERSION_CODES.Q..Build.VERSION_CODES.S_V2)) {
+ // We won't activate active resume mode when the API level is not in [Q, S_V2].
+ return
+ }
+ assertThat(
+ RetryingCameraStateOpener.shouldRetry(
+ ERROR_CAMERA_LIMIT_EXCEEDED,
+ 1,
+ DurationNs(1_000_000_000L),
+ camerasDisabledByDevicePolicy = false,
+ isForeground = true,
+ )
+ ).isTrue()
+
+ assertThat(
+ RetryingCameraStateOpener.shouldRetry(
+ ERROR_CAMERA_LIMIT_EXCEEDED,
+ 2,
+ DurationNs(30_000_000_000L), // 30s
+ camerasDisabledByDevicePolicy = false,
+ isForeground = true,
+ )
+ ).isTrue()
+ }
+
+ @Test
+ fun testShouldRetryCameraDisconnectedOnLongerTimeoutWhenActiveResume() {
+ if (Build.VERSION.SDK_INT !in (Build.VERSION_CODES.Q..Build.VERSION_CODES.S_V2)) {
+ // We won't activate active resume mode when the API level is not in [Q, S_V2].
+ return
+ }
+ assertThat(
+ RetryingCameraStateOpener.shouldRetry(
+ ERROR_CAMERA_DISCONNECTED,
+ 1,
+ DurationNs(1_000_000_000L),
+ camerasDisabledByDevicePolicy = false,
+ isForeground = true,
+ )
+ ).isTrue()
+
+ assertThat(
+ RetryingCameraStateOpener.shouldRetry(
+ ERROR_CAMERA_DISCONNECTED,
+ 2,
+ DurationNs(30_000_000_000L), // 30s
+ camerasDisabledByDevicePolicy = false,
+ isForeground = true,
+ )
+ ).isTrue()
+ }
+
+ @Test
+ fun testShouldActivateActiveResume() {
+ assertThat(
+ RetryingCameraStateOpener.shouldActivateActiveResume(
+ isForeground = false,
+ errorCode = ERROR_CAMERA_IN_USE,
+ )
+ ).isFalse()
+ if (Build.VERSION.SDK_INT in (Build.VERSION_CODES.Q..Build.VERSION_CODES.S_V2)) {
+ // Regardless of what error it is, we should only activate active resume mode when the
+ // API level is [Q, S_V2], where multi-resume is supported and camera access priority
+ // changes aren't properly notified.
+ assertThat(
+ RetryingCameraStateOpener.shouldActivateActiveResume(
+ isForeground = true,
+ errorCode = ERROR_CAMERA_SERVICE,
+ )
+ ).isFalse()
+ assertThat(
+ RetryingCameraStateOpener.shouldActivateActiveResume(
+ isForeground = true,
+ errorCode = ERROR_CAMERA_IN_USE,
+ )
+ ).isTrue()
+ assertThat(
+ RetryingCameraStateOpener.shouldActivateActiveResume(
+ isForeground = true,
+ errorCode = ERROR_CAMERA_LIMIT_EXCEEDED,
+ )
+ ).isTrue()
+ assertThat(
+ RetryingCameraStateOpener.shouldActivateActiveResume(
+ isForeground = true,
+ errorCode = ERROR_CAMERA_DISCONNECTED,
+ )
+ ).isTrue()
+ } else {
+ assertThat(
+ RetryingCameraStateOpener.shouldActivateActiveResume(
+ isForeground = true,
+ errorCode = ERROR_CAMERA_SERVICE,
+ )
+ ).isFalse()
+ assertThat(
+ RetryingCameraStateOpener.shouldActivateActiveResume(
+ isForeground = true,
+ errorCode = ERROR_CAMERA_IN_USE,
+ )
+ ).isFalse()
+ }
}
@Test
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCamera2DeviceCloser.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCamera2DeviceCloser.kt
index cfcc500..ca52b9b 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCamera2DeviceCloser.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCamera2DeviceCloser.kt
@@ -25,6 +25,7 @@
override fun closeCamera(
cameraDeviceWrapper: CameraDeviceWrapper?,
cameraDevice: CameraDevice?,
+ closeUnderError: Boolean,
androidCameraState: AndroidCameraState,
) {
cameraDeviceWrapper?.onDeviceClosed()
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 c8a6aa8..863ef6a 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
@@ -28,6 +28,7 @@
var surfaceMap: Map<StreamId, Surface>? = null
override val cameraId: CameraId
get() = CameraId.fromCamera2Id("0")
+ override var isForeground = true
override fun start() {
started = true
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDeviceWrapper.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDeviceWrapper.kt
index 76333bd..7a0093d 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDeviceWrapper.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDeviceWrapper.kt
@@ -28,6 +28,7 @@
import androidx.camera.camera2.pipe.compat.Api23Compat
import androidx.camera.camera2.pipe.compat.CameraCaptureSessionWrapper
import androidx.camera.camera2.pipe.compat.CameraDeviceWrapper
+import androidx.camera.camera2.pipe.compat.CameraExtensionSessionWrapper
import androidx.camera.camera2.pipe.compat.InputConfigData
import androidx.camera.camera2.pipe.compat.OutputConfigurationWrapper
import androidx.camera.camera2.pipe.compat.SessionConfigData
@@ -40,6 +41,7 @@
get() = fakeCamera.cameraId
var currentStateCallback: CameraCaptureSessionWrapper.StateCallback? = null
+ var currentExtensionStateCallback: CameraExtensionSessionWrapper.StateCallback? = null
var currentSession: FakeCaptureSessionWrapper? = null
override fun createCaptureRequest(template: RequestTemplate): CaptureRequest.Builder {
@@ -109,8 +111,14 @@
return true
}
+ override fun createExtensionSession(config: SessionConfigData): Boolean {
+ createFakeExtensionSession(config.extensionStateCallback)
+ return true
+ }
+
override fun onDeviceClosed() {
currentStateCallback?.onSessionFinalized()
+ currentExtensionStateCallback?.onSessionFinalized()
}
fun createFakeCaptureSession(
@@ -122,6 +130,15 @@
return nextSession
}
+ private fun createFakeExtensionSession(
+ stateCallback: CameraExtensionSessionWrapper.StateCallback? = null
+ ): FakeCaptureSessionWrapper {
+ val nextSession = FakeCaptureSessionWrapper(this)
+ currentSession = nextSession
+ currentExtensionStateCallback = stateCallback
+ return nextSession
+ }
+
@Suppress("UNCHECKED_CAST")
override fun <T : Any> unwrapAs(type: KClass<T>): T? =
when (type) {
diff --git a/camera/camera-camera2/build.gradle b/camera/camera-camera2/build.gradle
index 26856ba..8c0100e 100644
--- a/camera/camera-camera2/build.gradle
+++ b/camera/camera-camera2/build.gradle
@@ -45,7 +45,7 @@
testImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.3.1")
testImplementation(project(":camera:camera-video"))
testImplementation(project(":camera:camera-testing"))
- testImplementation("androidx.arch.core:core-testing:2.1.0")
+ testImplementation("androidx.arch.core:core-testing:2.2.0")
testImplementation(libs.junit) // Needed for Assert.assertThrows
testImplementation("org.codehaus.plexus:plexus-utils:3.4.1")
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplDeviceTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplDeviceTest.java
index 2f3e542..0a53825 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplDeviceTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplDeviceTest.java
@@ -59,6 +59,7 @@
import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
import androidx.camera.camera2.internal.compat.quirk.CameraQuirks;
import androidx.camera.camera2.internal.compat.workaround.AutoFlashAEModeDisabler;
+import androidx.camera.camera2.internal.util.TestUtil;
import androidx.camera.core.CameraControl;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.CameraXConfig;
@@ -431,7 +432,7 @@
imageCapture);
Camera2CameraControlImpl camera2CameraControlImpl =
- (Camera2CameraControlImpl) mCamera.getCameraControl();
+ TestUtil.getCamera2CameraControlImpl(mCamera.getCameraControl());
CameraCaptureCallback captureCallback = mock(CameraCaptureCallback.class);
CaptureConfig.Builder captureConfigBuilder = new CaptureConfig.Builder();
@@ -458,7 +459,7 @@
ApplicationProvider.getApplicationContext(), CameraSelector.DEFAULT_BACK_CAMERA,
imageAnalysis);
- return (Camera2CameraControlImpl) mCamera.getCameraControl();
+ return TestUtil.getCamera2CameraControlImpl(mCamera.getCameraControl());
}
private <T> void assertArraySize(T[] array, int expectedSize) {
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
index 1f1834bf..505da1e 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
@@ -64,6 +64,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
+import android.util.Range;
import android.view.Surface;
import androidx.annotation.NonNull;
@@ -232,8 +233,6 @@
@Before
public void setup() throws CameraAccessException, InterruptedException,
AssumptionViolatedException, TimeoutException, ExecutionException {
- mTestParameters0 = new CaptureSessionTestParameters("mTestParameters0");
- mTestParameters1 = new CaptureSessionTestParameters("mTestParameters1");
mHandler = new Handler(sHandlerThread.getLooper());
mExecutor = CameraXExecutors.newHandlerExecutor(mHandler);
@@ -254,6 +253,10 @@
} catch (CameraAccessExceptionCompat e) {
throw new AssumptionViolatedException("Could not retrieve camera characteristics", e);
}
+ mTestParameters0 = new CaptureSessionTestParameters("mTestParameters0",
+ mCameraCharacteristics);
+ mTestParameters1 = new CaptureSessionTestParameters("mTestParameters1",
+ mCameraCharacteristics);
mDynamicRangesCompat =
DynamicRangesCompat.fromCameraCharacteristics(mCameraCharacteristics);
@@ -684,8 +687,9 @@
// From CameraEventCallbacks option
assertThat(captureResult.getRequest().get(CaptureRequest.CONTROL_AF_MODE)).isEqualTo(
CaptureRequest.CONTROL_AF_MODE_MACRO);
- assertThat(captureResult.getRequest().get(CaptureRequest.FLASH_MODE)).isEqualTo(
- CaptureRequest.FLASH_MODE_TORCH);
+ assertThat(captureResult.getRequest().get(
+ CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION)).isEqualTo(
+ mTestParameters0.mEvRange.getLower());
// From SessionConfig option
assertThat(captureResult.getRequest().get(CaptureRequest.CONTROL_AE_MODE)).isEqualTo(
@@ -856,8 +860,9 @@
CaptureRequest.CONTROL_AF_MODE_OFF);
// From CameraEventCallbacks option
- assertThat(captureResult.getRequest().get(CaptureRequest.FLASH_MODE)).isEqualTo(
- CaptureRequest.FLASH_MODE_TORCH);
+ assertThat(captureResult.getRequest().get(
+ CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION)).isEqualTo(
+ mTestParameters0.mEvRange.getLower());
// From SessionConfig option
assertThat(captureResult.getRequest().get(CaptureRequest.CONTROL_AE_MODE)).isEqualTo(
@@ -1032,7 +1037,8 @@
assertThat(result1).isInstanceOf(Camera2CameraCaptureResult.class);
CaptureResult captureResult1 = ((Camera2CameraCaptureResult) result1).getCaptureResult();
assertThat(captureResult1.getRequest().get(
- CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION)).isEqualTo(0);
+ CaptureRequest.CONTROL_SCENE_MODE)).isEqualTo(
+ mTestParameters0.mTestCameraEventCallback.mAvailableSceneMode);
// The onDisableSession should not been invoked.
verify(mTestParameters0.mTestCameraEventCallback.mDisableCallback,
never()).onCaptureCompleted(any(CameraCaptureResult.class));
@@ -1051,7 +1057,8 @@
assertThat(result2).isInstanceOf(Camera2CameraCaptureResult.class);
CaptureResult captureResult2 = ((Camera2CameraCaptureResult) result2).getCaptureResult();
assertThat(captureResult2.getRequest().get(
- CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION)).isEqualTo(0);
+ CaptureRequest.CONTROL_SCENE_MODE)).isEqualTo(
+ mTestParameters0.mTestCameraEventCallback.mAvailableSceneMode);
// The onEnableSession should not been invoked in close().
verify(mTestParameters0.mTestCameraEventCallback.mEnableCallback,
never()).onCaptureCompleted(any(CameraCaptureResult.class));
@@ -1673,31 +1680,47 @@
*/
private static class TestCameraEventCallback extends CameraEventCallback {
+ TestCameraEventCallback(CameraCharacteristicsCompat characteristics) {
+ if (characteristics != null) {
+ int[] availableSceneModes =
+ characteristics.get(CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES);
+ if (availableSceneModes != null && availableSceneModes.length > 0) {
+ mAvailableSceneMode = availableSceneModes[0];
+ } else {
+ mAvailableSceneMode = CameraCharacteristics.CONTROL_SCENE_MODE_DISABLED;
+ }
+ } else {
+ mAvailableSceneMode = CameraCharacteristics.CONTROL_SCENE_MODE_DISABLED;
+ }
+ }
+
private final CameraCaptureCallback mEnableCallback = Mockito.mock(
CameraCaptureCallback.class);
private final CameraCaptureCallback mDisableCallback = Mockito.mock(
CameraCaptureCallback.class);
+ private final int mAvailableSceneMode;
+
@Override
public CaptureConfig onInitSession() {
- return getCaptureConfig(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0, null);
+ return getCaptureConfig(CaptureRequest.CONTROL_SCENE_MODE, mAvailableSceneMode, null);
}
@Override
public CaptureConfig onEnableSession() {
- return getCaptureConfig(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0,
+ return getCaptureConfig(CaptureRequest.CONTROL_SCENE_MODE, mAvailableSceneMode,
mEnableCallback);
}
@Override
public CaptureConfig onRepeating() {
- return getCaptureConfig(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0, null);
+ return getCaptureConfig(CaptureRequest.CONTROL_SCENE_MODE, mAvailableSceneMode, null);
}
@Override
public CaptureConfig onDisableSession() {
- return getCaptureConfig(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION,
- 0, mDisableCallback);
+ return getCaptureConfig(CaptureRequest.CONTROL_SCENE_MODE, mAvailableSceneMode,
+ mDisableCallback);
}
}
@@ -1795,8 +1818,7 @@
private final SessionConfig mSessionConfig;
private final CaptureConfig mCaptureConfig;
- private final TestCameraEventCallback mTestCameraEventCallback =
- new TestCameraEventCallback();
+ private final TestCameraEventCallback mTestCameraEventCallback;
private final CameraEventCallback mMockCameraEventCallback = Mockito.mock(
CameraEventCallback.class);
@@ -1810,6 +1832,7 @@
Mockito.mock(CameraCaptureSession.CaptureCallback.class);
private final DeferrableSurface mDeferrableSurface;
+ private final Range<Integer> mEvRange;
/**
* A composite capture callback that dispatches callbacks to both mock and real callbacks.
* The mock callback is used to verify the callback result. The real callback is used to
@@ -1825,7 +1848,7 @@
}
});
- CaptureSessionTestParameters(String name) {
+ CaptureSessionTestParameters(String name, CameraCharacteristicsCompat characteristics) {
mHandlerThread = new HandlerThread(name);
mHandlerThread.start();
mHandler = HandlerCompat.createAsync(mHandlerThread.getLooper());
@@ -1843,6 +1866,7 @@
builder.addRepeatingCameraCaptureCallback(
CaptureCallbackContainer.create(mCamera2CaptureCallback));
+ mTestCameraEventCallback = new TestCameraEventCallback(characteristics);
MutableOptionsBundle testCallbackConfig = MutableOptionsBundle.create();
testCallbackConfig.insertOption(Camera2ImplConfig.CAMERA_EVENT_CALLBACK_OPTION,
new CameraEventCallbacks(mTestCameraEventCallback));
@@ -1856,15 +1880,19 @@
// Set capture request options
// ==================================================================================
- // Priority | Component | AF_MODE | FLASH_MODE | AE_MODE
+ // Priority | Component | AF_MODE | EV MODE | AE_MODE
// ----------------------------------------------------------------------------------
// P1 | CaptureConfig | AF_MODE_OFF | |
// ----------------------------------------------------------------------------------
- // P2 | CameraEventCallbacks | AF_MODE_MACRO | FLASH_MODE_TORCH |
+ // P2 | CameraEventCallbacks | AF_MODE_MACRO | Min EV |
// ----------------------------------------------------------------------------------
- // P3 | SessionConfig | AF_MODE_AUTO | FLASH_MODE_SINGLE | AE_MODE_ON
+ // P3 | SessionConfig | AF_MODE_AUTO | Max EV | AE_MODE_ON
// ==================================================================================
+ mEvRange = characteristics != null
+ ? characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE)
+ : new Range<>(0, 0);
+
Camera2ImplConfig.Builder camera2ConfigBuilder = new Camera2ImplConfig.Builder();
// Add capture request options for CameraEventCallbacks
@@ -1878,8 +1906,8 @@
CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_MACRO)
.setCaptureRequestOption(
- CaptureRequest.FLASH_MODE,
- CaptureRequest.FLASH_MODE_TORCH)
+ CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION,
+ mEvRange.getLower())
.build());
return builder.build();
}
@@ -1893,7 +1921,7 @@
.setCaptureRequestOption(
CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO)
.setCaptureRequestOption(
- CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_SINGLE)
+ CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, mEvRange.getUpper())
.setCaptureRequestOption(
CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/TorchControlDeviceTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/TorchControlDeviceTest.java
index 8143d94..3e6a382 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/TorchControlDeviceTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/TorchControlDeviceTest.java
@@ -21,6 +21,7 @@
import android.content.Context;
import androidx.camera.camera2.Camera2Config;
+import androidx.camera.camera2.internal.util.TestUtil;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.CameraXConfig;
import androidx.camera.core.ImageAnalysis;
@@ -82,8 +83,8 @@
// Make ImageAnalysis active.
imageAnalysis.setAnalyzer(CameraXExecutors.mainThreadExecutor(), ImageProxy::close);
mCamera = CameraUtil.createCameraAndAttachUseCase(context, cameraSelector, imageAnalysis);
- Camera2CameraControlImpl cameraControl = (Camera2CameraControlImpl)
- mCamera.getCameraControl();
+ Camera2CameraControlImpl cameraControl =
+ TestUtil.getCamera2CameraControlImpl(mCamera.getCameraControl());
mTorchControl = cameraControl.getTorchControl();
}
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/util/TestUtil.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/util/TestUtil.java
new file mode 100644
index 0000000..cd4f5ef
--- /dev/null
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/util/TestUtil.java
@@ -0,0 +1,48 @@
+/*
+ * 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.internal.util;
+
+import androidx.annotation.RequiresApi;
+import androidx.camera.camera2.internal.Camera2CameraControlImpl;
+import androidx.camera.camera2.internal.Camera2CameraInfoImpl;
+import androidx.camera.core.CameraControl;
+import androidx.camera.core.CameraInfo;
+import androidx.camera.core.impl.CameraControlInternal;
+import androidx.camera.core.impl.CameraInfoInternal;
+
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class TestUtil {
+ public static Camera2CameraControlImpl getCamera2CameraControlImpl(
+ CameraControl cameraControl) {
+ if (cameraControl instanceof CameraControlInternal) {
+ CameraControlInternal impl =
+ ((CameraControlInternal) cameraControl).getImplementation();
+ return (Camera2CameraControlImpl) impl;
+ }
+ throw new IllegalArgumentException(
+ "Can't get Camera2CameraControlImpl from the CameraControl");
+ }
+
+ public static Camera2CameraInfoImpl getCamera2CameraInfoImpl(CameraInfo cameraInfo) {
+ if (cameraInfo instanceof CameraInfoInternal) {
+ CameraInfoInternal impl = ((CameraInfoInternal) cameraInfo).getImplementation();
+ return (Camera2CameraInfoImpl) impl;
+ }
+ throw new IllegalArgumentException(
+ "Can't get Camera2CameraInfoImpl from the CameraInfo");
+ }
+}
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/interop/Camera2CameraControlDeviceTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/interop/Camera2CameraControlDeviceTest.java
index 7977243..2566caf 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/interop/Camera2CameraControlDeviceTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/interop/Camera2CameraControlDeviceTest.java
@@ -39,9 +39,9 @@
import androidx.annotation.OptIn;
import androidx.camera.camera2.Camera2Config;
import androidx.camera.camera2.internal.Camera2CameraControlImpl;
+import androidx.camera.camera2.internal.util.TestUtil;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
-import androidx.camera.core.impl.CameraInfoInternal;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.internal.CameraUseCaseAdapter;
import androidx.camera.testing.CameraUtil;
@@ -94,7 +94,8 @@
mCameraSelector = new CameraSelector.Builder().requireLensFacing(
CameraSelector.LENS_FACING_BACK).build();
mCamera = CameraUtil.createCameraUseCaseAdapter(mContext, mCameraSelector);
- mCamera2CameraControlImpl = (Camera2CameraControlImpl) mCamera.getCameraControl();
+ mCamera2CameraControlImpl =
+ TestUtil.getCamera2CameraControlImpl(mCamera.getCameraControl());
mCamera2CameraControl = mCamera2CameraControlImpl.getCamera2CameraControl();
mMockCaptureCallback = mock(CameraCaptureSession.CaptureCallback.class);
}
@@ -267,7 +268,7 @@
private Rect getZoom2XCropRegion() throws Exception {
AtomicReference<String> cameraIdRef = new AtomicReference<>();
- String cameraId = ((CameraInfoInternal) mCamera.getCameraInfo()).getCameraId();
+ String cameraId = TestUtil.getCamera2CameraInfoImpl(mCamera.getCameraInfo()).getCameraId();
cameraIdRef.set(cameraId);
CameraManager cameraManager =
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
index da4070a..5481b76 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
@@ -78,6 +78,7 @@
sessionBuilder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW);
break;
case VIDEO_CAPTURE:
+ case STREAM_SHARING:
sessionBuilder.setTemplateType(CameraDevice.TEMPLATE_RECORD);
break;
}
@@ -101,6 +102,7 @@
captureBuilder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW);
break;
case VIDEO_CAPTURE:
+ case STREAM_SHARING:
captureBuilder.setTemplateType(CameraDevice.TEMPLATE_RECORD);
break;
}
@@ -125,7 +127,7 @@
int targetRotation = mDisplayInfoManager.getMaxSizeDisplay().getRotation();
mutableConfig.insertOption(OPTION_TARGET_ROTATION, targetRotation);
- if (captureType == CaptureType.VIDEO_CAPTURE) {
+ if (captureType == CaptureType.VIDEO_CAPTURE || captureType == CaptureType.STREAM_SHARING) {
mutableConfig.insertOption(OPTION_ZSL_DISABLED, true);
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraCharacteristicsApi28Impl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraCharacteristicsApi28Impl.java
index 6e9ed1d..860400e 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraCharacteristicsApi28Impl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraCharacteristicsApi28Impl.java
@@ -20,12 +20,14 @@
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
+import androidx.camera.core.Logger;
+import java.util.Collections;
import java.util.Set;
@RequiresApi(28)
class CameraCharacteristicsApi28Impl extends CameraCharacteristicsBaseImpl{
-
+ private static final String TAG = "CameraCharacteristicsImpl";
CameraCharacteristicsApi28Impl(@NonNull CameraCharacteristics cameraCharacteristics) {
super(cameraCharacteristics);
}
@@ -33,6 +35,14 @@
@NonNull
@Override
public Set<String> getPhysicalCameraIds() {
- return mCameraCharacteristics.getPhysicalCameraIds();
+ try {
+ return mCameraCharacteristics.getPhysicalCameraIds();
+ } catch (Exception e) {
+ // getPhysicalCameraIds could cause crash in Robolectric and there is no known
+ // workaround
+ Logger.e(TAG,
+ "CameraCharacteristics.getPhysicalCameraIds throws an exception.", e);
+ return Collections.emptySet();
+ }
}
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/Camera2CameraControl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/Camera2CameraControl.java
index 3591ef3..0b8f4b9 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/Camera2CameraControl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/Camera2CameraControl.java
@@ -25,6 +25,7 @@
import androidx.camera.camera2.internal.Camera2CameraControlImpl;
import androidx.camera.camera2.internal.annotation.CameraExecutor;
import androidx.camera.core.CameraControl;
+import androidx.camera.core.impl.CameraControlInternal;
import androidx.camera.core.impl.Config;
import androidx.camera.core.impl.TagBundle;
import androidx.camera.core.impl.annotation.ExecutedBy;
@@ -125,9 +126,11 @@
*/
@NonNull
public static Camera2CameraControl from(@NonNull CameraControl cameraControl) {
- Preconditions.checkArgument(cameraControl instanceof Camera2CameraControlImpl,
+ CameraControlInternal cameraControlImpl =
+ ((CameraControlInternal) cameraControl).getImplementation();
+ Preconditions.checkArgument(cameraControlImpl instanceof Camera2CameraControlImpl,
"CameraControl doesn't contain Camera2 implementation.");
- return ((Camera2CameraControlImpl) cameraControl).getCamera2CameraControl();
+ return ((Camera2CameraControlImpl) cameraControlImpl).getCamera2CameraControl();
}
/**
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/Camera2CameraInfo.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/Camera2CameraInfo.java
index 5e18f87..33522d2 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/Camera2CameraInfo.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/Camera2CameraInfo.java
@@ -25,6 +25,7 @@
import androidx.annotation.RestrictTo.Scope;
import androidx.camera.camera2.internal.Camera2CameraInfoImpl;
import androidx.camera.core.CameraInfo;
+import androidx.camera.core.impl.CameraInfoInternal;
import androidx.core.util.Preconditions;
import java.util.Map;
@@ -58,9 +59,11 @@
*/
@NonNull
public static Camera2CameraInfo from(@NonNull CameraInfo cameraInfo) {
- Preconditions.checkArgument(cameraInfo instanceof Camera2CameraInfoImpl,
+ CameraInfoInternal cameraInfoImpl =
+ ((CameraInfoInternal) cameraInfo).getImplementation();
+ Preconditions.checkArgument(cameraInfoImpl instanceof Camera2CameraInfoImpl,
"CameraInfo doesn't contain Camera2 implementation.");
- return ((Camera2CameraInfoImpl) cameraInfo).getCamera2CameraInfo();
+ return ((Camera2CameraInfoImpl) cameraInfoImpl).getCamera2CameraInfo();
}
/**
@@ -119,9 +122,10 @@
@NonNull
public static CameraCharacteristics extractCameraCharacteristics(
@NonNull CameraInfo cameraInfo) {
- Preconditions.checkState(cameraInfo instanceof Camera2CameraInfoImpl, "CameraInfo does "
- + "not contain any Camera2 information.");
- Camera2CameraInfoImpl impl = (Camera2CameraInfoImpl) cameraInfo;
+ CameraInfoInternal cameraInfoImpl = ((CameraInfoInternal) cameraInfo).getImplementation();
+ Preconditions.checkState(cameraInfoImpl instanceof Camera2CameraInfoImpl,
+ "CameraInfo does not contain any Camera2 information.");
+ Camera2CameraInfoImpl impl = (Camera2CameraInfoImpl) cameraInfoImpl;
return impl.getCameraCharacteristicsCompat().toCameraCharacteristics();
}
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
index 8e0b364..b11b2a5a 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
@@ -2127,6 +2127,8 @@
ImageFormat.PRIVATE,
PREVIEW_SIZE,
SDR,
+ listOf(CaptureType.PREVIEW),
+ useCase.currentConfig,
/*targetFrameRate=*/null
)
val attachedAnalysis = AttachedSurfaceInfo.create(
@@ -2137,6 +2139,8 @@
ImageFormat.YUV_420_888,
RECORD_SIZE,
SDR,
+ listOf(CaptureType.IMAGE_ANALYSIS),
+ useCase.currentConfig,
/*targetFrameRate=*/null
)
@@ -2172,6 +2176,8 @@
ImageFormat.PRIVATE,
PREVIEW_SIZE,
DynamicRange(FORMAT_HDR10, BIT_DEPTH_10_BIT),
+ listOf(CaptureType.PREVIEW),
+ useCase.currentConfig,
/*targetFrameRate=*/null
)
val attachedPriv2 = AttachedSurfaceInfo.create(
@@ -2182,6 +2188,8 @@
ImageFormat.YUV_420_888,
RECORD_SIZE,
DynamicRange(FORMAT_HDR10_PLUS, BIT_DEPTH_10_BIT),
+ listOf(CaptureType.VIDEO_CAPTURE),
+ useCase.currentConfig,
/*targetFrameRate=*/null
)
@@ -2226,6 +2234,8 @@
ImageFormat.PRIVATE,
PREVIEW_SIZE,
SDR,
+ listOf(CaptureType.PREVIEW),
+ useCase.currentConfig,
/*targetFrameRate=*/null
)
val attachedPriv2 = AttachedSurfaceInfo.create(
@@ -2236,6 +2246,8 @@
ImageFormat.YUV_420_888,
RECORD_SIZE,
SDR,
+ listOf(CaptureType.IMAGE_ANALYSIS),
+ useCase.currentConfig,
/*targetFrameRate=*/null
)
@@ -2465,6 +2477,8 @@
ImageFormat.JPEG,
Size(1280, 720),
SDR,
+ listOf(CaptureType.PREVIEW),
+ useCase.currentConfig,
Range(40, 50)
)
getSuggestedSpecsAndVerify(
@@ -2492,6 +2506,8 @@
ImageFormat.JPEG,
Size(1280, 720),
SDR,
+ listOf(CaptureType.PREVIEW),
+ useCase.currentConfig,
Range(40, 50)
)
getSuggestedSpecsAndVerify(
@@ -2519,6 +2535,8 @@
ImageFormat.JPEG,
Size(1280, 720),
SDR,
+ listOf(CaptureType.PREVIEW),
+ useCase.currentConfig,
Range(40, 50)
)
getSuggestedSpecsAndVerify(
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2CameraInfoTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2CameraInfoTest.java
index 2276320..8ad3050 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2CameraInfoTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2CameraInfoTest.java
@@ -83,6 +83,7 @@
Camera2CameraInfo camera2CameraInfo = mock(Camera2CameraInfo.class);
Camera2CameraInfoImpl cameraInfoImpl = mock(Camera2CameraInfoImpl.class);
when(cameraInfoImpl.getCamera2CameraInfo()).thenAnswer(ignored -> camera2CameraInfo);
+ when(cameraInfoImpl.getImplementation()).thenAnswer(ignored -> cameraInfoImpl);
Camera2CameraInfo resultCamera2CameraInfo = Camera2CameraInfo.from(cameraInfoImpl);
assertThat(resultCamera2CameraInfo).isEqualTo(camera2CameraInfo);
diff --git a/camera/camera-core/build.gradle b/camera/camera-core/build.gradle
index 56a6ebb..c789d6d 100644
--- a/camera/camera-core/build.gradle
+++ b/camera/camera-core/build.gradle
@@ -47,6 +47,8 @@
testImplementation(libs.truth)
testImplementation(libs.robolectric)
testImplementation(libs.mockitoCore4)
+ testImplementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
+ testImplementation(project(":internal-testutils-truth"))
testImplementation(project(":camera:camera-testing"), {
exclude group: "androidx.camera", module: "camera-core"
})
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/FakeOtherUseCaseConfig.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/FakeOtherUseCaseConfig.java
index 722a214..0369ec0 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/FakeOtherUseCaseConfig.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/FakeOtherUseCaseConfig.java
@@ -27,6 +27,7 @@
import androidx.camera.core.impl.OptionsBundle;
import androidx.camera.core.impl.SessionConfig;
import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.impl.UseCaseConfigFactory;
import java.util.UUID;
@@ -56,6 +57,12 @@
return retrieveOption(OPTION_SURFACE_OCCUPANCY_PRIORITY);
}
+ @NonNull
+ @Override
+ public UseCaseConfigFactory.CaptureType getCaptureType() {
+ return UseCaseConfigFactory.CaptureType.PREVIEW;
+ }
+
@Override
public int getInputFormat() {
return ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
@@ -190,5 +197,12 @@
getMutableConfig().insertOption(OPTION_HIGH_RESOLUTION_DISABLED, disabled);
return this;
}
+
+ @NonNull
+ @Override
+ public Builder setCaptureType(@NonNull UseCaseConfigFactory.CaptureType captureType) {
+ getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
+ return this;
+ }
}
}
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
index a385c37..d39f3e1 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
@@ -33,6 +33,7 @@
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Rect;
+import android.os.Build;
import android.provider.MediaStore;
import android.util.Rational;
import android.util.Size;
@@ -42,6 +43,7 @@
import androidx.camera.core.concurrent.CameraCoordinator;
import androidx.camera.core.impl.CameraCaptureCallback;
import androidx.camera.core.impl.CameraCaptureMetaData;
+import androidx.camera.core.impl.CameraControlInternal;
import androidx.camera.core.impl.CaptureConfig;
import androidx.camera.core.impl.ImageCaptureConfig;
import androidx.camera.core.impl.StreamSpec;
@@ -99,6 +101,9 @@
@Before
public void setup() {
+ assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes"
+ + " the test failure.",
+ Build.MODEL.equalsIgnoreCase("wembley") && Build.VERSION.SDK_INT <= 30);
FakeCamera fakeCamera = new FakeCamera("fakeCameraId");
FakeCameraDeviceSurfaceManager fakeCameraDeviceSurfaceManager =
@@ -142,7 +147,7 @@
ImageCapture.OnImageCapturedCallback callback = mock(
ImageCapture.OnImageCapturedCallback.class);
FakeCameraControl fakeCameraControl =
- ((FakeCameraControl) mCameraUseCaseAdapter.getCameraControl());
+ getCameraControlImplementation(mCameraUseCaseAdapter.getCameraControl());
fakeCameraControl.setOnNewCaptureRequestListener(captureConfigs -> {
// Notify the cancel after the capture request has been successfully submitted
@@ -173,7 +178,7 @@
ImageCapture.OnImageCapturedCallback callback = mock(
ImageCapture.OnImageCapturedCallback.class);
FakeCameraControl fakeCameraControl =
- ((FakeCameraControl) mCameraUseCaseAdapter.getCameraControl());
+ getCameraControlImplementation(mCameraUseCaseAdapter.getCameraControl());
fakeCameraControl.setOnNewCaptureRequestListener(captureConfigs -> {
// Notify the failure after the capture request has been successfully submitted
fakeCameraControl.notifyAllRequestsOnCaptureFailed();
@@ -309,6 +314,11 @@
assertThat(hasJpegQuality(captureConfigs, jpegQuality)).isTrue();
}
+ private FakeCameraControl getCameraControlImplementation(CameraControl cameraControl) {
+ CameraControlInternal impl = ((CameraControlInternal) cameraControl).getImplementation();
+ return (FakeCameraControl) impl;
+ }
+
@NonNull
private List<CaptureConfig> captureImage(@NonNull ImageCapture imageCapture,
@NonNull Class<?> callbackClass) {
@@ -342,7 +352,7 @@
}
FakeCameraControl fakeCameraControl =
- ((FakeCameraControl) mCameraUseCaseAdapter.getCameraControl());
+ getCameraControlImplementation(mCameraUseCaseAdapter.getCameraControl());
FakeCameraControl.OnNewCaptureRequestListener mockCaptureRequestListener =
mock(FakeCameraControl.OnNewCaptureRequestListener.class);
fakeCameraControl.setOnNewCaptureRequestListener(mockCaptureRequestListener);
@@ -510,7 +520,7 @@
ImageCapture.OnImageCapturedCallback callback = mock(
ImageCapture.OnImageCapturedCallback.class);
FakeCameraControl fakeCameraControl =
- ((FakeCameraControl) mCameraUseCaseAdapter.getCameraControl());
+ getCameraControlImplementation(mCameraUseCaseAdapter.getCameraControl());
CountDownLatch latch = new CountDownLatch(1);
fakeCameraControl.setOnNewCaptureRequestListener(captureConfigs -> {
latch.countDown();
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt
index 2515afd..47df690 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt
@@ -157,7 +157,7 @@
takePictureCallback,
Futures.immediateFuture(null)
)
- val input = ProcessingNode.InputPacket.of(processingRequest, imageIn, false)
+ val input = ProcessingNode.InputPacket.of(processingRequest, imageIn)
// Act and return.
nodeIn.edge.accept(input)
return if (outputFileOptions == null) {
@@ -193,7 +193,7 @@
CameraCaptureResultImageInfo(CAMERA_CAPTURE_RESULT),
createJpegBytes(WIDTH, HEIGHT)
)
- val input = ProcessingNode.InputPacket.of(processingRequest, imageIn, false)
+ val input = ProcessingNode.InputPacket.of(processingRequest, imageIn)
// Act and return.
nodeIn.edge.accept(input)
val filePath = takePictureCallback.getOnDiskResult().savedUri!!.path!!
@@ -232,7 +232,7 @@
createJpegBytes(WIDTH, HEIGHT)
)
// Act.
- val input = ProcessingNode.InputPacket.of(processingRequest, imageIn, false)
+ val input = ProcessingNode.InputPacket.of(processingRequest, imageIn)
// Act and return.
nodeIn.edge.accept(input)
// Assert: the output image is identical to the input.
@@ -266,7 +266,7 @@
takePictureCallback,
Futures.immediateFuture(null)
)
- val input = ProcessingNode.InputPacket.of(processingRequest, imageIn, false)
+ val input = ProcessingNode.InputPacket.of(processingRequest, imageIn)
// Act: send input to the edge and wait for the saved URI
nodeIn.edge.accept(input)
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/DefaultSurfaceProcessorTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/DefaultSurfaceProcessorTest.kt
index 60d5d804..daa23c4 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/DefaultSurfaceProcessorTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/DefaultSurfaceProcessorTest.kt
@@ -21,10 +21,8 @@
import android.graphics.Rect
import android.graphics.SurfaceTexture
import android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW
-import android.os.Build
import android.util.Size
import android.view.Surface
-import androidx.annotation.RequiresApi
import androidx.camera.core.CameraEffect
import androidx.camera.core.ImageProxy
import androidx.camera.core.ImageReaderProxys
@@ -38,6 +36,7 @@
import androidx.camera.testing.HandlerUtil
import androidx.camera.testing.TestImageUtil.createBitmap
import androidx.camera.testing.TestImageUtil.getAverageDiff
+import androidx.camera.testing.TestImageUtil.rotateBitmap
import androidx.camera.testing.fakes.FakeCamera
import androidx.concurrent.futures.await
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -137,14 +136,13 @@
}
}
- @RequiresApi(Build.VERSION_CODES.M)
@Test
fun snapshotAndRelease_futureReceivesException(): Unit = runBlocking {
// Arrange: create DefaultSurfaceProcessor and setup input/output Surface.
createSurfaceProcessor()
// Act: take a snapshot and then release the processor.
- val snapshotFuture = surfaceProcessor.snapshot(JPEG_QUALITY)
+ val snapshotFuture = surfaceProcessor.snapshot(JPEG_QUALITY, 0)
surfaceProcessor.release()
// Assert: the snapshot future should receive an exception.
@@ -159,7 +157,7 @@
}
}
- @RequiresApi(Build.VERSION_CODES.M)
+ @SdkSuppress(minSdkVersion = 23)
@Test
fun snapshot_JpegWrittenToSurface(): Unit = runBlocking {
// Arrange: create DefaultSurfaceProcessor and setup input/output Surface.
@@ -175,9 +173,10 @@
format = ImageFormat.JPEG
)
surfaceProcessor.onOutputSurface(surfaceOutput)
+ val rotationDegrees = 90
- // Act: take a snapshot and draw a Bitmap to the input Surface
- surfaceProcessor.snapshot(JPEG_QUALITY)
+ // Act: draw a Bitmap to the input Surface and take a snapshot with 90 degrees rotation.
+ surfaceProcessor.snapshot(JPEG_QUALITY, rotationDegrees)
val inputImage = createBitmap(WIDTH, HEIGHT)
val inputSurface = surfaceRequest.deferrableSurface.surface.get()
val canvas = inputSurface.lockHardwareCanvas()
@@ -190,7 +189,8 @@
val bytes = ByteArray(byteBuffer.remaining())
byteBuffer.get(bytes)
val outputImage = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
- assertThat(getAverageDiff(outputImage, inputImage)).isEqualTo(0)
+ val expectedImage = rotateBitmap(inputImage, rotationDegrees)
+ assertThat(getAverageDiff(outputImage, expectedImage)).isEqualTo(0)
// Cleanup.
surfaceRequest.deferrableSurface.close()
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/FocusMeteringAction.java b/camera/camera-core/src/main/java/androidx/camera/core/FocusMeteringAction.java
index 30f3edb..2faa545 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/FocusMeteringAction.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/FocusMeteringAction.java
@@ -186,6 +186,17 @@
}
/**
+ * Create a Builder from a {@link FocusMeteringAction}.
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public Builder(@NonNull FocusMeteringAction focusMeteringAction) {
+ mMeteringPointsAf.addAll(focusMeteringAction.getMeteringPointsAf());
+ mMeteringPointsAe.addAll(focusMeteringAction.getMeteringPointsAe());
+ mMeteringPointsAwb.addAll(focusMeteringAction.getMeteringPointsAwb());
+ mAutoCancelDurationInMillis = focusMeteringAction.getAutoCancelDurationInMillis();
+ }
+
+ /**
* Adds another {@link MeteringPoint} with default metering mode {@link #FLAG_AF} |
* {@link #FLAG_AE} | {@link #FLAG_AWB}.
*
@@ -271,6 +282,27 @@
}
/**
+ *
+ * Remove all points of the given meteringMode.
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @NonNull
+ public Builder removePoints(@MeteringMode int meteringMode) {
+ if ((meteringMode & FLAG_AF) != 0) {
+ mMeteringPointsAf.clear();
+ }
+
+ if ((meteringMode & FLAG_AE) != 0) {
+ mMeteringPointsAe.clear();
+ }
+
+ if ((meteringMode & FLAG_AWB) != 0) {
+ mMeteringPointsAwb.clear();
+ }
+ return this;
+ }
+
+ /**
* Builds the {@link FocusMeteringAction} instance.
*/
@NonNull
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
index c3c0d6e..b7268b0 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
@@ -32,6 +32,7 @@
import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ROTATION;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAMERA_SELECTOR;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_CONFIG_UNPACKER;
+import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_DEFAULT_CAPTURE_CONFIG;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_DEFAULT_SESSION_CONFIG;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_HIGH_RESOLUTION_DISABLED;
@@ -771,7 +772,7 @@
public UseCaseConfig<?> getDefaultConfig(boolean applyDefaultConfig,
@NonNull UseCaseConfigFactory factory) {
Config captureConfig = factory.getConfig(
- UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS,
+ DEFAULT_CONFIG.getConfig().getCaptureType(),
ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY);
if (applyDefaultConfig) {
@@ -1022,7 +1023,8 @@
.setDefaultResolution(DEFAULT_TARGET_RESOLUTION)
.setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
.setTargetAspectRatio(DEFAULT_ASPECT_RATIO)
- .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR);
+ .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR)
+ .setCaptureType(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS);
DEFAULT_CONFIG = builder.getUseCaseConfig();
}
@@ -1600,5 +1602,13 @@
getMutableConfig().insertOption(OPTION_HIGH_RESOLUTION_DISABLED, disabled);
return this;
}
+
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ @Override
+ public Builder setCaptureType(@NonNull UseCaseConfigFactory.CaptureType captureType) {
+ getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
+ return this;
+ }
}
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
index 4eb88f5..b547104 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -42,6 +42,7 @@
import static androidx.camera.core.impl.ImageOutputConfig.OPTION_CUSTOM_ORDERED_RESOLUTIONS;
import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAMERA_SELECTOR;
+import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_HIGH_RESOLUTION_DISABLED;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_ZSL_DISABLED;
import static androidx.camera.core.impl.utils.Threads.checkMainThread;
@@ -513,7 +514,7 @@
public UseCaseConfig<?> getDefaultConfig(boolean applyDefaultConfig,
@NonNull UseCaseConfigFactory factory) {
Config captureConfig = factory.getConfig(
- UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE,
+ DEFAULT_CONFIG.getConfig().getCaptureType(),
getCaptureMode());
if (applyDefaultConfig) {
@@ -2024,7 +2025,8 @@
Builder builder = new Builder()
.setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
.setTargetAspectRatio(DEFAULT_ASPECT_RATIO)
- .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR);
+ .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR)
+ .setCaptureType(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE);
DEFAULT_CONFIG = builder.getUseCaseConfig();
}
@@ -3077,5 +3079,16 @@
getMutableConfig().insertOption(OPTION_HIGH_RESOLUTION_DISABLED, disabled);
return this;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ @Override
+ public Builder setCaptureType(@NonNull UseCaseConfigFactory.CaptureType captureType) {
+ getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
+ return this;
+ }
}
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
index 02d7366..153f731 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
@@ -39,6 +39,7 @@
import static androidx.camera.core.impl.PreviewConfig.OPTION_TARGET_ROTATION;
import static androidx.camera.core.impl.PreviewConfig.OPTION_USE_CASE_EVENT_CALLBACK;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAMERA_SELECTOR;
+import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_HIGH_RESOLUTION_DISABLED;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_TARGET_FRAME_RATE;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_ZSL_DISABLED;
@@ -582,7 +583,7 @@
public UseCaseConfig<?> getDefaultConfig(boolean applyDefaultConfig,
@NonNull UseCaseConfigFactory factory) {
Config captureConfig = factory.getConfig(
- UseCaseConfigFactory.CaptureType.PREVIEW,
+ DEFAULT_CONFIG.getConfig().getCaptureType(),
ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY);
if (applyDefaultConfig) {
@@ -780,7 +781,8 @@
Builder builder = new Builder()
.setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
.setTargetAspectRatio(DEFAULT_ASPECT_RATIO)
- .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR);
+ .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR)
+ .setCaptureType(UseCaseConfigFactory.CaptureType.PREVIEW);
DEFAULT_CONFIG = builder.getUseCaseConfig();
}
@@ -1245,5 +1247,13 @@
getMutableConfig().insertOption(OPTION_HIGH_RESOLUTION_DISABLED, disabled);
return this;
}
+
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ @Override
+ public Builder setCaptureType(@NonNull UseCaseConfigFactory.CaptureType captureType) {
+ getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
+ return this;
+ }
}
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java
index a14dfdf..ee8e36c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java
@@ -143,8 +143,7 @@
inputEdge.getRequestEdge().setListener(requestConsumer);
inputEdge.getErrorEdge().setListener(this::sendCaptureError);
- mOutputEdge = Out.of(inputEdge.getInputFormat(), inputEdge.getOutputFormat(),
- inputEdge.isVirtualCamera());
+ mOutputEdge = Out.of(inputEdge.getInputFormat(), inputEdge.getOutputFormat());
return mOutputEdge;
}
@@ -399,14 +398,9 @@
*/
abstract int getOutputFormat();
- /**
- * Whether the pipeline is connected to a virtual camera.
- */
- abstract boolean isVirtualCamera();
-
- static Out of(int inputFormat, int outputFormat, boolean isVirtualCamera) {
+ static Out of(int inputFormat, int outputFormat) {
return new AutoValue_CaptureNode_Out(new Edge<>(), new Edge<>(), inputFormat,
- outputFormat, isVirtualCamera);
+ outputFormat);
}
}
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingInput2Packet.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingInput2Packet.java
index 318f126..4109418 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingInput2Packet.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingInput2Packet.java
@@ -74,9 +74,7 @@
throw new ImageCaptureException(ERROR_FILE_IO, "Failed to extract EXIF data.", e);
}
}
- if (EXIF_ROTATION_AVAILABILITY.shouldUseExifOrientation(image)
- && !inputPacket.isVirtualCamera()) {
- // Virtual camera doesn't respect the CaptureRequest rotation degrees.
+ if (EXIF_ROTATION_AVAILABILITY.shouldUseExifOrientation(image)) {
checkNotNull(exif, "JPEG image must have exif.");
return createPacketWithHalRotation(request, exif, image);
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java
index ba7717d..c55f5ce 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java
@@ -233,15 +233,9 @@
@NonNull
abstract ImageProxy getImageProxy();
- /**
- * Whether the pipeline is connected to a virtual camera.
- */
- abstract boolean isVirtualCamera();
-
static InputPacket of(@NonNull ProcessingRequest processingRequest,
- @NonNull ImageProxy imageProxy, boolean isVirtualCamera) {
- return new AutoValue_ProcessingNode_InputPacket(processingRequest, imageProxy,
- isVirtualCamera);
+ @NonNull ImageProxy imageProxy) {
+ return new AutoValue_ProcessingNode_InputPacket(processingRequest, imageProxy);
}
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java
index f9d7238..481fe56 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java
@@ -43,12 +43,10 @@
ProcessingRequest mPendingRequest;
private ProcessingNode.In mOutputEdge;
- private boolean mIsVirtualCamera;
@NonNull
@Override
public ProcessingNode.In transform(@NonNull CaptureNode.Out captureNodeOut) {
- mIsVirtualCamera = captureNodeOut.isVirtualCamera();
// Listen to input edges.
captureNodeOut.getImageEdge().setListener(this::matchImageWithRequest);
captureNodeOut.getRequestEdge().setListener(this::trackIncomingRequest);
@@ -97,8 +95,7 @@
mPendingRequest.getTagBundleKey()));
checkState(stageId == mPendingRequest.getStageIds().get(0));
- mOutputEdge.getEdge().accept(
- ProcessingNode.InputPacket.of(mPendingRequest, imageProxy, mIsVirtualCamera));
+ mOutputEdge.getEdge().accept(ProcessingNode.InputPacket.of(mPendingRequest, imageProxy));
mPendingRequest = null;
}
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/AttachedSurfaceInfo.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/AttachedSurfaceInfo.java
index cdde2a1..8fe27de 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/AttachedSurfaceInfo.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/AttachedSurfaceInfo.java
@@ -25,6 +25,9 @@
import androidx.camera.core.DynamicRange;
import com.google.auto.value.AutoValue;
+
+import java.util.List;
+
/**
* Container object for holding {@link SurfaceConfig} and its attributed ImageFormat,
* {@link Size}, and target Frame Rate {@link Range}
@@ -45,9 +48,11 @@
int imageFormat,
@NonNull Size size,
@NonNull DynamicRange dynamicRange,
+ @NonNull List<UseCaseConfigFactory.CaptureType> captureTypes,
+ @Nullable Config implementationOptions,
@Nullable Range<Integer> targetFrameRate) {
return new AutoValue_AttachedSurfaceInfo(surfaceConfig, imageFormat, size,
- dynamicRange, targetFrameRate);
+ dynamicRange, captureTypes, implementationOptions, targetFrameRate);
}
/** Returns the SurfaceConfig. */
@@ -65,6 +70,16 @@
@NonNull
public abstract DynamicRange getDynamicRange();
+ /** Returns the capture types of this surface. Multiple capture types represent a
+ * {@link androidx.camera.core.streamsharing.StreamSharing} and its children.*/
+ @SuppressWarnings("AutoValueImmutableFields")
+ @NonNull
+ public abstract List<UseCaseConfigFactory.CaptureType> getCaptureTypes();
+
+ /** Returns the implementations of this surface. */
+ @Nullable
+ public abstract Config getImplementationOptions();
+
/** Returns the configuration target frame rate. */
@Nullable
public abstract Range<Integer> getTargetFrameRate();
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java
index 33688e9..c89bb2f 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java
@@ -39,9 +39,13 @@
/**
* The CameraControlInternal Interface.
*
+ *
* <p>CameraControlInternal is used for global camera operations like zoom, focus, flash and
- * triggering
- * AF/AE.
+ * triggering AF/AE as well as some internal operations.
+ *
+ * <p>{@link #getImplementation()} returns a {@link CameraControlInternal} instance
+ * that contains the actual implementation and can be cast to an implementation specific class.
+ * If the instance itself is the implementation instance, then it should return <code>this</code>.
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public interface CameraControlInternal extends CameraControl {
@@ -136,6 +140,16 @@
@NonNull
Config getInteropConfig();
+ /**
+ * Gets the underlying implementation instance which could be cast into an implementation
+ * specific class for further use in implementation module. Returns <code>this</code> if this
+ * instance is the implementation instance.
+ */
+ @NonNull
+ default CameraControlInternal getImplementation() {
+ return this;
+ }
+
CameraControlInternal DEFAULT_EMPTY_INSTANCE = new CameraControlInternal() {
@FlashMode
@Override
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java
index 791efe0..e6a1e9b 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java
@@ -36,6 +36,10 @@
* An interface for retrieving camera information.
*
* <p>Contains methods for retrieving characteristics for a specific camera.
+ *
+ * <p>{@link #getImplementation()} returns a {@link CameraInfoInternal} instance
+ * that contains the actual implementation and can be cast to an implementation specific class.
+ * If the instance itself is the implementation instance, then it should return <code>this</code>.
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public interface CameraInfoInternal extends CameraInfo {
@@ -101,6 +105,17 @@
@NonNull
Set<DynamicRange> getSupportedDynamicRanges();
+ /**
+ * Gets the underlying implementation instance which could be cast into an implementation
+ * specific class for further use in implementation module. Returns <code>this</code> if this
+ * instance is the implementation instance.
+ */
+ @NonNull
+ default CameraInfoInternal getImplementation() {
+ return this;
+ }
+
+
/** {@inheritDoc} */
@NonNull
@Override
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraControl.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraControl.java
new file mode 100644
index 0000000..ea73135
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraControl.java
@@ -0,0 +1,155 @@
+/*
+ * 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.core.impl;
+
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.FocusMeteringAction;
+import androidx.camera.core.FocusMeteringResult;
+import androidx.camera.core.ImageCapture;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.List;
+
+/**
+ * A {@link CameraControlInternal} that forwards all the calls into the given
+ * {@link CameraControlInternal}.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class ForwardingCameraControl implements CameraControlInternal {
+ private final CameraControlInternal mCameraControlInternal;
+
+ /**
+ * Create an instance that will forward all calls to the supplied {@link CameraControlInternal}
+ * instance.
+ */
+ public ForwardingCameraControl(@NonNull CameraControlInternal cameraControlInternal) {
+ mCameraControlInternal = cameraControlInternal;
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> enableTorch(boolean torch) {
+ return mCameraControlInternal.enableTorch(torch);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<FocusMeteringResult> startFocusAndMetering(
+ @NonNull FocusMeteringAction action) {
+ return mCameraControlInternal.startFocusAndMetering(action);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> cancelFocusAndMetering() {
+ return mCameraControlInternal.cancelFocusAndMetering();
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> setZoomRatio(float ratio) {
+ return mCameraControlInternal.setZoomRatio(ratio);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> setLinearZoom(float linearZoom) {
+ return mCameraControlInternal.setLinearZoom(linearZoom);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Integer> setExposureCompensationIndex(int value) {
+ return mCameraControlInternal.setExposureCompensationIndex(value);
+ }
+
+ @Override
+ @ImageCapture.FlashMode
+ public int getFlashMode() {
+ return mCameraControlInternal.getFlashMode();
+ }
+
+ @Override
+ public void setFlashMode(@ImageCapture.FlashMode int flashMode) {
+ mCameraControlInternal.setFlashMode(flashMode);
+ }
+
+ @Override
+ public void addZslConfig(@NonNull SessionConfig.Builder sessionConfigBuilder) {
+ mCameraControlInternal.addZslConfig(sessionConfigBuilder);
+ }
+
+ @Override
+ public void setZslDisabledByUserCaseConfig(boolean disabled) {
+ mCameraControlInternal.setZslDisabledByUserCaseConfig(disabled);
+ }
+
+ @Override
+ public boolean isZslDisabledByByUserCaseConfig() {
+ return mCameraControlInternal.isZslDisabledByByUserCaseConfig();
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<List<Void>> submitStillCaptureRequests(
+ @NonNull List<CaptureConfig> captureConfigs,
+ @ImageCapture.CaptureMode int captureMode,
+ @ImageCapture.FlashType int flashType) {
+ return mCameraControlInternal.submitStillCaptureRequests(
+ captureConfigs,
+ captureMode,
+ flashType);
+ }
+
+ @NonNull
+ @Override
+ public SessionConfig getSessionConfig() {
+ return mCameraControlInternal.getSessionConfig();
+ }
+
+ @NonNull
+ @Override
+ public Rect getSensorRect() {
+ return mCameraControlInternal.getSensorRect();
+ }
+
+ @Override
+ public void addInteropConfig(@NonNull Config config) {
+ mCameraControlInternal.addInteropConfig(config);
+ }
+
+ @Override
+ public void clearInteropConfig() {
+ mCameraControlInternal.clearInteropConfig();
+ }
+
+ @NonNull
+ @Override
+ public Config getInteropConfig() {
+ return mCameraControlInternal.getInteropConfig();
+ }
+
+ @NonNull
+ @Override
+ public CameraControlInternal getImplementation() {
+ return mCameraControlInternal.getImplementation();
+ }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraInfo.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraInfo.java
new file mode 100644
index 0000000..c389a1d
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraInfo.java
@@ -0,0 +1,195 @@
+/*
+ * 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.core.impl;
+
+import android.util.Range;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.CameraSelector;
+import androidx.camera.core.CameraState;
+import androidx.camera.core.DynamicRange;
+import androidx.camera.core.ExperimentalZeroShutterLag;
+import androidx.camera.core.ExposureState;
+import androidx.camera.core.FocusMeteringAction;
+import androidx.camera.core.ZoomState;
+import androidx.lifecycle.LiveData;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+/**
+ * A {@link CameraInfoInternal} that forwards all the calls into the given
+ * {@link CameraInfoInternal}.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class ForwardingCameraInfo implements CameraInfoInternal {
+
+ private final CameraInfoInternal mCameraInfoInternal;
+
+ /**
+ * Create an instance that will forward all calls to the supplied {@link CameraInfoInternal}
+ * instance.
+ */
+ public ForwardingCameraInfo(@NonNull CameraInfoInternal cameraInfoInternal) {
+ mCameraInfoInternal = cameraInfoInternal;
+ }
+
+ @Override
+ public int getSensorRotationDegrees() {
+ return mCameraInfoInternal.getSensorRotationDegrees();
+ }
+
+ @Override
+ public int getSensorRotationDegrees(int relativeRotation) {
+ return mCameraInfoInternal.getSensorRotationDegrees(relativeRotation);
+ }
+
+ @Override
+ public boolean hasFlashUnit() {
+ return mCameraInfoInternal.hasFlashUnit();
+ }
+
+ @NonNull
+ @Override
+ public LiveData<Integer> getTorchState() {
+ return mCameraInfoInternal.getTorchState();
+ }
+
+ @NonNull
+ @Override
+ public LiveData<ZoomState> getZoomState() {
+ return mCameraInfoInternal.getZoomState();
+ }
+
+ @NonNull
+ @Override
+ public ExposureState getExposureState() {
+ return mCameraInfoInternal.getExposureState();
+ }
+
+ @NonNull
+ @Override
+ public LiveData<CameraState> getCameraState() {
+ return mCameraInfoInternal.getCameraState();
+ }
+
+ @NonNull
+ @Override
+ public String getImplementationType() {
+ return mCameraInfoInternal.getImplementationType();
+ }
+
+ @Override
+ public int getLensFacing() {
+ return mCameraInfoInternal.getLensFacing();
+ }
+
+ @Override
+ public float getIntrinsicZoomRatio() {
+ return mCameraInfoInternal.getIntrinsicZoomRatio();
+ }
+
+ @Override
+ public boolean isFocusMeteringSupported(@NonNull FocusMeteringAction action) {
+ return mCameraInfoInternal.isFocusMeteringSupported(action);
+ }
+
+ @Override
+ @ExperimentalZeroShutterLag
+ public boolean isZslSupported() {
+ return mCameraInfoInternal.isZslSupported();
+ }
+
+ @NonNull
+ @Override
+ public Set<Range<Integer>> getSupportedFrameRateRanges() {
+ return mCameraInfoInternal.getSupportedFrameRateRanges();
+ }
+
+ @Override
+ public boolean isPrivateReprocessingSupported() {
+ return mCameraInfoInternal.isPrivateReprocessingSupported();
+ }
+
+ @NonNull
+ @Override
+ public String getCameraId() {
+ return mCameraInfoInternal.getCameraId();
+ }
+
+ @Override
+ public void addSessionCaptureCallback(@NonNull Executor executor,
+ @NonNull CameraCaptureCallback callback) {
+ mCameraInfoInternal.addSessionCaptureCallback(executor, callback);
+ }
+
+ @Override
+ public void removeSessionCaptureCallback(@NonNull CameraCaptureCallback callback) {
+ mCameraInfoInternal.removeSessionCaptureCallback(callback);
+ }
+
+ @NonNull
+ @Override
+ public Quirks getCameraQuirks() {
+ return mCameraInfoInternal.getCameraQuirks();
+ }
+
+ @NonNull
+ @Override
+ public EncoderProfilesProvider getEncoderProfilesProvider() {
+ return mCameraInfoInternal.getEncoderProfilesProvider();
+ }
+
+ @NonNull
+ @Override
+ public Timebase getTimebase() {
+ return mCameraInfoInternal.getTimebase();
+ }
+
+ @NonNull
+ @Override
+ public List<Size> getSupportedResolutions(int format) {
+ return mCameraInfoInternal.getSupportedResolutions(format);
+ }
+
+ @NonNull
+ @Override
+ public List<Size> getSupportedHighResolutions(int format) {
+ return mCameraInfoInternal.getSupportedHighResolutions(format);
+ }
+
+ @NonNull
+ @Override
+ public Set<DynamicRange> getSupportedDynamicRanges() {
+ return mCameraInfoInternal.getSupportedDynamicRanges();
+ }
+
+ @NonNull
+ @Override
+ public CameraInfoInternal getImplementation() {
+ return mCameraInfoInternal.getImplementation();
+ }
+
+ @NonNull
+ @Override
+ public CameraSelector getCameraSelector() {
+ return mCameraInfoInternal.getCameraSelector();
+ }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageInputConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageInputConfig.java
index 57a4094..7e1b2a2 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageInputConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageInputConfig.java
@@ -64,4 +64,34 @@
DynamicRange.UNSPECIFIED));
}
+ /**
+ * Returns whether a dynamic range was set on this config.
+ *
+ * <p>This method can be used to determine whether a dynamic range has been set to override
+ * the default {@link DynamicRange#UNSPECIFIED}.
+ */
+ default boolean hasDynamicRange() {
+ return containsOption(OPTION_INPUT_DYNAMIC_RANGE);
+ }
+
+ /**
+ * Builder for a {@link ImageInputConfig}.
+ *
+ * @param <B> The top level builder type for which this builder is composed with.
+ */
+ interface Builder<B> {
+ /**
+ * Sets the dynamic range required for images from this configuration.
+ *
+ * <p>Valid values depend on the specific configuration. The default behavior when not
+ * set is to automatically choose a dynamic range based on device capabilities and the
+ * dynamic range requested by other use cases, but use cases should override that
+ * behavior if needed.
+ *
+ * @param dynamicRange The dynamic range required for this configuration.
+ * @return The current Builder.
+ */
+ @NonNull
+ B setDynamicRange(@NonNull DynamicRange dynamicRange);
+ }
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/RestrictedCameraControl.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/RestrictedCameraControl.java
new file mode 100644
index 0000000..6fc61f8
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/RestrictedCameraControl.java
@@ -0,0 +1,199 @@
+/*
+ * 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.core.impl;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.FocusMeteringAction;
+import androidx.camera.core.FocusMeteringResult;
+import androidx.camera.core.impl.utils.futures.Futures;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A {@link CameraControlInternal} whose capabilities can be restricted via
+ * {@link #enableRestrictedOperations(boolean, Set)}.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class RestrictedCameraControl extends ForwardingCameraControl {
+ /**
+ * Defines the list of supported camera operations.
+ */
+ public static final int ZOOM = 0;
+ public static final int AUTO_FOCUS = 1;
+ public static final int AF_REGION = 2;
+ public static final int AE_REGION = 3;
+ public static final int AWB_REGION = 4;
+ public static final int FLASH = 5;
+ public static final int TORCH = 6;
+ public static final int EXPOSURE_COMPENSATION = 7;
+
+ public @interface CameraOperation {
+ }
+
+ private final CameraControlInternal mCameraControl;
+ private volatile boolean mUseRestrictedCameraOperations = false;
+ @Nullable
+ private volatile @CameraOperation Set<Integer> mRestrictedCameraOperations;
+
+ /**
+ * Creates the restricted version of the given {@link CameraControlInternal}.
+ */
+ public RestrictedCameraControl(@NonNull CameraControlInternal cameraControl) {
+ super(cameraControl);
+ mCameraControl = cameraControl;
+ }
+
+ /**
+ * Enable or disable the restricted operations. If disabled, it works just like the origin
+ * CameraControlInternal instance.
+ */
+ public void enableRestrictedOperations(boolean enable,
+ @Nullable @CameraOperation Set<Integer> restrictedOperations) {
+ mUseRestrictedCameraOperations = enable;
+ mRestrictedCameraOperations = restrictedOperations;
+ }
+
+ /**
+ * Returns implementation instance.
+ */
+ @NonNull
+ @Override
+ public CameraControlInternal getImplementation() {
+ return mCameraControl;
+ }
+
+ boolean isOperationSupported(
+ @NonNull @CameraOperation int... operations) {
+ if (!mUseRestrictedCameraOperations || mRestrictedCameraOperations == null) {
+ return true;
+ }
+
+ // Arrays.asList doesn't work for int array.
+ List<Integer> operationList = new ArrayList<>(operations.length);
+ for (int operation : operations) {
+ operationList.add(operation);
+ }
+
+ return mRestrictedCameraOperations.containsAll(operationList);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> enableTorch(boolean torch) {
+ if (!isOperationSupported(TORCH)) {
+ return Futures.immediateFailedFuture(
+ new IllegalStateException("Torch is not supported"));
+ }
+ return mCameraControl.enableTorch(torch);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<FocusMeteringResult> startFocusAndMetering(
+ @NonNull FocusMeteringAction action) {
+ FocusMeteringAction modifiedAction = getModifiedFocusMeteringAction(action);
+ if (modifiedAction == null) {
+ return Futures.immediateFailedFuture(
+ new IllegalStateException("FocusMetering is not supported"));
+ }
+
+ return mCameraControl.startFocusAndMetering(modifiedAction);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> cancelFocusAndMetering() {
+ return mCameraControl.cancelFocusAndMetering();
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> setZoomRatio(float ratio) {
+ if (!isOperationSupported(ZOOM)) {
+ return Futures.immediateFailedFuture(
+ new IllegalStateException("Zoom is not supported"));
+ }
+ return mCameraControl.setZoomRatio(ratio);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> setLinearZoom(float linearZoom) {
+ if (!isOperationSupported(ZOOM)) {
+ return Futures.immediateFailedFuture(
+ new IllegalStateException("Zoom is not supported"));
+ }
+ return mCameraControl.setLinearZoom(linearZoom);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Integer> setExposureCompensationIndex(int value) {
+ if (!isOperationSupported(EXPOSURE_COMPENSATION)) {
+ return Futures.immediateFailedFuture(
+ new IllegalStateException("ExposureCompensation is not supported"));
+ }
+ return mCameraControl.setExposureCompensationIndex(value);
+ }
+
+ /**
+ * Returns the modified {@link FocusMeteringAction} that filters out unsupported AE/AF/AWB
+ * regions. Returns null if none of AF/AE/AWB regions can be supported after the filtering.
+ */
+ @Nullable
+ FocusMeteringAction getModifiedFocusMeteringAction(@NonNull FocusMeteringAction action) {
+ boolean shouldModify = false;
+ FocusMeteringAction.Builder builder = new FocusMeteringAction.Builder(action);
+ if (!action.getMeteringPointsAf().isEmpty()
+ && !isOperationSupported(AUTO_FOCUS, AF_REGION)) {
+ shouldModify = true;
+ builder.removePoints(FocusMeteringAction.FLAG_AF);
+ }
+
+ if (!action.getMeteringPointsAe().isEmpty()
+ && !isOperationSupported(AE_REGION)) {
+ shouldModify = true;
+ builder.removePoints(FocusMeteringAction.FLAG_AE);
+ }
+
+ if (!action.getMeteringPointsAwb().isEmpty()
+ && !isOperationSupported(AWB_REGION)) {
+ shouldModify = true;
+ builder.removePoints(FocusMeteringAction.FLAG_AWB);
+ }
+
+ // Returns origin action if no need to modify.
+ if (!shouldModify) {
+ return action;
+ }
+
+ FocusMeteringAction modifyAction = builder.build();
+ if (modifyAction.getMeteringPointsAf().isEmpty()
+ && modifyAction.getMeteringPointsAe().isEmpty()
+ && modifyAction.getMeteringPointsAwb().isEmpty()) {
+ // All regions are not allowed, return null.
+ return null;
+ }
+ return builder.build();
+ }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/RestrictedCameraInfo.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/RestrictedCameraInfo.java
new file mode 100644
index 0000000..b2148f5
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/RestrictedCameraInfo.java
@@ -0,0 +1,123 @@
+/*
+ * 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.core.impl;
+
+import android.util.Range;
+import android.util.Rational;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.ExposureState;
+import androidx.camera.core.FocusMeteringAction;
+import androidx.camera.core.TorchState;
+import androidx.camera.core.ZoomState;
+import androidx.camera.core.internal.ImmutableZoomState;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+
+/**
+ * A {@link CameraInfoInternal} that returns disabled state if the corresponding operation in the
+ * given {@link RestrictedCameraControl} is disabled.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class RestrictedCameraInfo extends ForwardingCameraInfo {
+ private final CameraInfoInternal mCameraInfo;
+ private final RestrictedCameraControl mRestrictedCameraControl;
+
+ public RestrictedCameraInfo(@NonNull CameraInfoInternal cameraInfo,
+ @NonNull RestrictedCameraControl restrictedCameraControl) {
+ super(cameraInfo);
+ mCameraInfo = cameraInfo;
+ mRestrictedCameraControl = restrictedCameraControl;
+ }
+
+ @NonNull
+ @Override
+ public CameraInfoInternal getImplementation() {
+ return mCameraInfo;
+ }
+
+ @Override
+ public boolean hasFlashUnit() {
+ if (!mRestrictedCameraControl.isOperationSupported(RestrictedCameraControl.FLASH)) {
+ return false;
+ }
+
+ return mCameraInfo.hasFlashUnit();
+ }
+
+ @NonNull
+ @Override
+ public LiveData<Integer> getTorchState() {
+ if (!mRestrictedCameraControl.isOperationSupported(RestrictedCameraControl.TORCH)) {
+ return new MutableLiveData<>(TorchState.OFF);
+ }
+
+ return mCameraInfo.getTorchState();
+ }
+
+ @NonNull
+ @Override
+ public LiveData<ZoomState> getZoomState() {
+ if (!mRestrictedCameraControl.isOperationSupported(RestrictedCameraControl.ZOOM)) {
+ return new MutableLiveData<>(ImmutableZoomState.create(
+ /* zoomRatio */1f, /* maxZoomRatio */ 1f,
+ /* minZoomRatio */ 1f, /* linearZoom*/ 0f));
+ }
+ return mCameraInfo.getZoomState();
+ }
+
+ @NonNull
+ @Override
+ public ExposureState getExposureState() {
+ if (!mRestrictedCameraControl.isOperationSupported(
+ RestrictedCameraControl.EXPOSURE_COMPENSATION)) {
+ return new ExposureState() {
+ @Override
+ public int getExposureCompensationIndex() {
+ return 0;
+ }
+
+ @NonNull
+ @Override
+ public Range<Integer> getExposureCompensationRange() {
+ return new Range<>(0, 0);
+ }
+
+ @NonNull
+ @Override
+ public Rational getExposureCompensationStep() {
+ return Rational.ZERO;
+ }
+
+ @Override
+ public boolean isExposureCompensationSupported() {
+ return false;
+ }
+ };
+ }
+ return mCameraInfo.getExposureState();
+ }
+
+ @Override
+ public boolean isFocusMeteringSupported(@NonNull FocusMeteringAction action) {
+ if (mRestrictedCameraControl.getModifiedFocusMeteringAction(action) == null) {
+ return false;
+ }
+ return mCameraInfo.isFocusMeteringSupported(action);
+ }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionProcessor.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionProcessor.java
index f9858f5..70b5c2b 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionProcessor.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionProcessor.java
@@ -24,7 +24,9 @@
import androidx.annotation.RequiresApi;
import androidx.camera.core.CameraInfo;
+import java.util.Collections;
import java.util.Map;
+import java.util.Set;
/**
* A processor for (1) transforming the surfaces used in Preview/ImageCapture/ImageAnalysis
@@ -127,6 +129,14 @@
}
/**
+ * Returns the supported camera operations when the SessionProcessor is enabled.
+ */
+ @NonNull
+ default @RestrictedCameraControl.CameraOperation Set<Integer> getSupportedCameraOperations() {
+ return Collections.emptySet();
+ }
+
+ /**
* Callback for {@link #startRepeating} and {@link #startCapture}.
*/
interface CaptureCallback {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfig.java
index 43d3f9e..7e13965 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfig.java
@@ -94,6 +94,12 @@
Option<Boolean> OPTION_HIGH_RESOLUTION_DISABLED =
Option.create("camerax.core.useCase.highResolutionDisabled", boolean.class);
+ /**
+ * Option: camerax.core.useCase.highResolutionDisabled
+ */
+ Option<UseCaseConfigFactory.CaptureType> OPTION_CAPTURE_TYPE = Option.create(
+ "camerax.core.useCase.captureType", UseCaseConfigFactory.CaptureType.class);
+
// *********************************************************************************************
@@ -314,6 +320,14 @@
}
/**
+ * @return The {@link UseCaseConfigFactory.CaptureType} of this UseCaseConfig.
+ */
+ @NonNull
+ default UseCaseConfigFactory.CaptureType getCaptureType() {
+ return retrieveOption(OPTION_CAPTURE_TYPE);
+ }
+
+ /**
* Builder for a {@link UseCase}.
*
* @param <T> The type of the object which will be built by {@link #build()}.
@@ -420,6 +434,14 @@
B setHighResolutionDisabled(boolean disabled);
/**
+ * Sets the capture type for this configuration.
+ *
+ * @param captureType The capture type for this use case.
+ */
+ @NonNull
+ B setCaptureType(@NonNull UseCaseConfigFactory.CaptureType captureType);
+
+ /**
* Retrieves the configuration used by this builder.
*
* @return the configuration used by this builder.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfigFactory.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfigFactory.java
index 209a9aa..480eb29 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfigFactory.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/UseCaseConfigFactory.java
@@ -50,7 +50,12 @@
/**
* Capture type for video capture. A use case of this type is consuming a stream of frames.
*/
- VIDEO_CAPTURE
+ VIDEO_CAPTURE,
+
+ /**
+ * Capture type for stream sharing. A use case of this type is consuming a stream of frames.
+ */
+ STREAM_SHARING
}
/**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
index 9d3021c..2350fbc 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
@@ -59,6 +59,10 @@
import androidx.camera.core.impl.CameraInternal;
import androidx.camera.core.impl.CameraMode;
import androidx.camera.core.impl.Config;
+import androidx.camera.core.impl.RestrictedCameraControl;
+import androidx.camera.core.impl.RestrictedCameraControl.CameraOperation;
+import androidx.camera.core.impl.RestrictedCameraInfo;
+import androidx.camera.core.impl.SessionProcessor;
import androidx.camera.core.impl.StreamSpec;
import androidx.camera.core.impl.SurfaceConfig;
import androidx.camera.core.impl.UseCaseConfig;
@@ -143,6 +147,12 @@
@Nullable
private StreamSharing mStreamSharing;
+ @NonNull
+ private final RestrictedCameraControl mRestrictedCameraControl;
+ @NonNull
+ private final RestrictedCameraInfo mRestrictedCameraInfo;
+
+
/**
* Create a new {@link CameraUseCaseAdapter} instance.
*
@@ -167,6 +177,12 @@
mCameraCoordinator = cameraCoordinator;
mCameraDeviceSurfaceManager = cameraDeviceSurfaceManager;
mUseCaseConfigFactory = useCaseConfigFactory;
+ // TODO(b/279996499): bind the same restricted CameraControl and CameraInfo to use cases.
+ mRestrictedCameraControl =
+ new RestrictedCameraControl(mCameraInternal.getCameraControlInternal());
+ mRestrictedCameraInfo =
+ new RestrictedCameraInfo(mCameraInternal.getCameraInfoInternal(),
+ mRestrictedCameraControl);
}
/**
@@ -593,6 +609,8 @@
existingSurfaces.add(AttachedSurfaceInfo.create(surfaceConfig,
useCase.getImageFormat(), useCase.getAttachedSurfaceResolution(),
Preconditions.checkNotNull(useCase.getAttachedStreamSpec()).getDynamicRange(),
+ getCaptureTypes(useCase),
+ useCase.getAttachedStreamSpec().getImplementationOptions(),
useCase.getCurrentConfig().getTargetFrameRate(null)));
suggestedStreamSpecs.put(useCase, useCase.getAttachedStreamSpec());
}
@@ -603,14 +621,14 @@
Map<UseCaseConfig<?>, List<Size>> configToSupportedSizesMap = new HashMap<>();
Rect sensorRect;
try {
- sensorRect = ((CameraControlInternal) getCameraControl()).getSensorRect();
+ sensorRect = mCameraInternal.getCameraControlInternal().getSensorRect();
} catch (NullPointerException e) {
// TODO(b/274531208): Remove the unnecessary SENSOR_INFO_ACTIVE_ARRAY_SIZE NPE
// check related code only which is used for robolectric tests
sensorRect = null;
}
SupportedOutputSizesSorter supportedOutputSizesSorter = new SupportedOutputSizesSorter(
- (CameraInfoInternal) getCameraInfo(),
+ cameraInfoInternal,
sensorRect != null ? rectToSize(sensorRect) : null);
for (UseCase useCase : newUseCases) {
ConfigPair configPair = configPairMap.get(useCase);
@@ -656,6 +674,19 @@
}
}
+ @NonNull
+ private static List<UseCaseConfigFactory.CaptureType> getCaptureTypes(UseCase useCase) {
+ List<UseCaseConfigFactory.CaptureType> result = new ArrayList<>();
+ if (isStreamSharing(useCase)) {
+ for (UseCase child : ((StreamSharing) useCase).getChildren()) {
+ result.add(child.getCurrentConfig().getCaptureType());
+ }
+ } else {
+ result.add(useCase.getCurrentConfig().getCaptureType());
+ }
+ return result;
+ }
+
/**
* Sets effects on the given {@link UseCase} list and returns unused effects.
*/
@@ -809,13 +840,13 @@
@NonNull
@Override
public CameraControl getCameraControl() {
- return mCameraInternal.getCameraControlInternal();
+ return mRestrictedCameraControl;
}
@NonNull
@Override
public CameraInfo getCameraInfo() {
- return mCameraInternal.getCameraInfoInternal();
+ return mRestrictedCameraInfo;
}
@NonNull
@@ -846,6 +877,14 @@
}
mCameraConfig = cameraConfig;
+ SessionProcessor sessionProcessor = mCameraConfig.getSessionProcessor(null);
+ if (sessionProcessor != null) {
+ @CameraOperation Set<Integer> supportedOps =
+ sessionProcessor.getSupportedCameraOperations();
+ mRestrictedCameraControl.enableRestrictedOperations(true, supportedOps);
+ } else {
+ mRestrictedCameraControl.enableRestrictedOperations(false, null);
+ }
//Configure the CameraInternal as well so that it can get SessionProcessor.
mCameraInternal.setExtendedConfig(mCameraConfig);
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/DefaultSurfaceProcessor.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/DefaultSurfaceProcessor.java
index bfce459..1eea449 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/DefaultSurfaceProcessor.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/DefaultSurfaceProcessor.java
@@ -18,6 +18,7 @@
import static androidx.camera.core.ImageProcessingUtil.writeJpegBytesToSurface;
import static androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
+import static androidx.camera.core.impl.utils.TransformUtils.rotateSize;
import static androidx.core.util.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
@@ -28,7 +29,6 @@
import android.opengl.Matrix;
import android.os.Handler;
import android.os.HandlerThread;
-import android.util.Pair;
import android.util.Size;
import android.view.Surface;
@@ -42,17 +42,21 @@
import androidx.camera.core.SurfaceOutput;
import androidx.camera.core.SurfaceProcessor;
import androidx.camera.core.SurfaceRequest;
+import androidx.camera.core.impl.utils.MatrixExt;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.impl.utils.futures.Futures;
import androidx.concurrent.futures.CallbackToFutureAdapter;
import androidx.core.util.Supplier;
+import com.google.auto.value.AutoValue;
import com.google.common.util.concurrent.ListenableFuture;
import kotlin.Triple;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -90,8 +94,7 @@
// Only access this on GL thread.
private boolean mIsReleased = false;
// Only access this on GL thread.
- private final List<Pair<CallbackToFutureAdapter.Completer<Void>, Integer>> mPendingSnapshots =
- new ArrayList<>();
+ private final List<PendingSnapshot> mPendingSnapshots = new ArrayList<>();
/** Constructs {@link DefaultSurfaceProcessor} with default shaders. */
DefaultSurfaceProcessor() {
@@ -180,13 +183,15 @@
});
}
- @NonNull
@Override
- public ListenableFuture<Void> snapshot(@IntRange(from = 0, to = 100) int jpegQuality) {
+ @NonNull
+ public ListenableFuture<Void> snapshot(
+ @IntRange(from = 0, to = 100) int jpegQuality,
+ @IntRange(from = 0, to = 359) int rotationDegrees) {
return Futures.nonCancellationPropagating(CallbackToFutureAdapter.getFuture(
completer -> {
- Pair<CallbackToFutureAdapter.Completer<Void>, Integer> pendingSnapshot =
- new Pair<>(completer, jpegQuality);
+ PendingSnapshot pendingSnapshot = PendingSnapshot.of(jpegQuality,
+ rotationDegrees, completer);
executeSafely(
() -> mPendingSnapshots.add(pendingSnapshot),
() -> completer.setException(new Exception(
@@ -240,6 +245,7 @@
*
* @param jpegOutput The <Surface, Surface size, transform matrix> tuple for drawing.
*/
+ @WorkerThread
private void takeSnapshotAndDrawJpeg(@Nullable Triple<Surface, Size, float[]> jpegOutput) {
if (mPendingSnapshots.isEmpty()) {
// No pending snapshot requests, do nothing.
@@ -252,48 +258,72 @@
return;
}
- // Get snapshot Bitmap.
- Bitmap bitmap = getBitmap(jpegOutput.getSecond(), jpegOutput.getThird());
-
// Write to JPEG surface, once for each snapshot request.
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- byte[] jpegBytes = null;
- int jpegQuality = -1;
- for (Pair<CallbackToFutureAdapter.Completer<Void>, Integer> pendingSnapshot :
- mPendingSnapshots) {
- if (jpegQuality != pendingSnapshot.second) {
- // If the quality is different, re-encode the bitmap.
- outputStream.reset();
- bitmap.compress(Bitmap.CompressFormat.JPEG, pendingSnapshot.second, outputStream);
- jpegBytes = outputStream.toByteArray();
- jpegQuality = pendingSnapshot.second;
+ try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+ byte[] jpegBytes = null;
+ int jpegQuality = -1;
+ int rotationDegrees = -1;
+ Bitmap bitmap = null;
+ Iterator<PendingSnapshot> iterator = mPendingSnapshots.iterator();
+ while (iterator.hasNext()) {
+ PendingSnapshot pendingSnapshot = iterator.next();
+ // Take a new snapshot if the rotation is different.
+ if (rotationDegrees != pendingSnapshot.getRotationDegrees() || bitmap == null) {
+ rotationDegrees = pendingSnapshot.getRotationDegrees();
+ // Recycle the previous bitmap to free up memory.
+ if (bitmap != null) {
+ bitmap.recycle();
+ }
+ bitmap = getBitmap(jpegOutput.getSecond(), jpegOutput.getThird(),
+ rotationDegrees);
+ // Clear JPEG quality to force re-encoding.
+ jpegQuality = -1;
+ }
+ // Re-encode the bitmap if the quality is different.
+ if (jpegQuality != pendingSnapshot.getJpegQuality()) {
+ outputStream.reset();
+ jpegQuality = pendingSnapshot.getJpegQuality();
+ bitmap.compress(Bitmap.CompressFormat.JPEG, jpegQuality, outputStream);
+ jpegBytes = outputStream.toByteArray();
+ }
+ writeJpegBytesToSurface(jpegOutput.getFirst(), requireNonNull(jpegBytes));
+ pendingSnapshot.getCompleter().set(null);
+ iterator.remove();
}
- writeJpegBytesToSurface(jpegOutput.getFirst(), requireNonNull(jpegBytes));
- pendingSnapshot.first.set(null);
+ } catch (IOException e) {
+ failAllPendingSnapshots(e);
}
- mPendingSnapshots.clear();
}
private void failAllPendingSnapshots(@NonNull Throwable throwable) {
- for (Pair<CallbackToFutureAdapter.Completer<Void>, Integer> pendingSnapshot :
- mPendingSnapshots) {
- pendingSnapshot.first.setException(throwable);
+ for (PendingSnapshot pendingSnapshot : mPendingSnapshots) {
+ pendingSnapshot.getCompleter().setException(throwable);
}
mPendingSnapshots.clear();
}
@NonNull
- private Bitmap getBitmap(@NonNull Size size, @NonNull float[] textureTransform) {
+ private Bitmap getBitmap(@NonNull Size size,
+ @NonNull float[] textureTransform,
+ int rotationDegrees) {
// Flip the snapshot. This is for reverting the GL transform added in SurfaceOutputImpl.
float[] snapshotTransform = new float[16];
// TODO(b/278109696): move GL flipping to MatrixExt.
Matrix.setIdentityM(snapshotTransform, 0);
Matrix.translateM(snapshotTransform, 0, 0f, 1f, 0f);
Matrix.scaleM(snapshotTransform, 0, 1f, -1f, 1f);
+
+ // Rotate the output if requested.
+ MatrixExt.preRotate(snapshotTransform, rotationDegrees, 0.5f, 0.5f);
+
+ // Apply the texture transform.
Matrix.multiplyMM(snapshotTransform, 0, snapshotTransform, 0, textureTransform, 0);
+
+ // Update the size based on the rotation degrees.
+ size = rotateSize(size, rotationDegrees);
+
// Take a snapshot Bitmap and compress it to JPEG.
return mGlRenderer.snapshot(size, snapshotTransform);
-
}
@WorkerThread
@@ -303,9 +333,8 @@
for (SurfaceOutput surfaceOutput : mOutputSurfaces.keySet()) {
surfaceOutput.close();
}
- for (Pair<CallbackToFutureAdapter.Completer<Void>, Integer> pendingSnapshot :
- mPendingSnapshots) {
- pendingSnapshot.first.setException(
+ for (PendingSnapshot pendingSnapshot : mPendingSnapshots) {
+ pendingSnapshot.getCompleter().setException(
new Exception("Failed to snapshot: DefaultSurfaceProcessor is released."));
}
mOutputSurfaces.clear();
@@ -362,6 +391,31 @@
}
/**
+ * A pending snapshot request to be executed on the next frame available.
+ */
+ @AutoValue
+ abstract static class PendingSnapshot {
+
+ @IntRange(from = 0, to = 100)
+ abstract int getJpegQuality();
+
+ @IntRange(from = 0, to = 359)
+ abstract int getRotationDegrees();
+
+ @NonNull
+ abstract CallbackToFutureAdapter.Completer<Void> getCompleter();
+
+ @NonNull
+ static AutoValue_DefaultSurfaceProcessor_PendingSnapshot of(
+ @IntRange(from = 0, to = 100) int jpegQuality,
+ @IntRange(from = 0, to = 359) int rotationDegrees,
+ @NonNull CallbackToFutureAdapter.Completer<Void> completer) {
+ return new AutoValue_DefaultSurfaceProcessor_PendingSnapshot(
+ jpegQuality, rotationDegrees, completer);
+ }
+ }
+
+ /**
* Factory class that produces {@link DefaultSurfaceProcessor}.
*
* <p> This is for working around the limit that OpenGL cannot be initialized in unit tests.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorInternal.java
index 0dd1f6da..3b512c3 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorInternal.java
@@ -52,7 +52,9 @@
* Takes a snapshot of the next available frame and write it to JPEG outputs.
*/
@NonNull
- default ListenableFuture<Void> snapshot(@IntRange(from = 0, to = 100) int jpegQuality) {
+ default ListenableFuture<Void> snapshot(
+ @IntRange(from = 0, to = 100) int jpegQuality,
+ @IntRange(from = 0, to = 359) int rotationDegrees) {
return Futures.immediateFuture(null);
}
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorWithExecutor.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorWithExecutor.java
index 862de4e..8eb4fe1 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorWithExecutor.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorWithExecutor.java
@@ -22,6 +22,7 @@
import android.os.Build;
+import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
@@ -100,7 +101,9 @@
@NonNull
@Override
- public ListenableFuture<Void> snapshot(int jpegQuality) {
+ public ListenableFuture<Void> snapshot(
+ @IntRange(from = 0, to = 100) int jpegQuality,
+ @IntRange(from = 0, to = 359) int rotationDegrees) {
return immediateFailedFuture(
new Exception("Snapshot not supported by external SurfaceProcessor"));
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java
index 4ba6851..93cd629 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java
@@ -20,6 +20,7 @@
import static androidx.camera.core.CameraEffect.VIDEO_CAPTURE;
import static androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_FORMAT;
+import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE;
import static androidx.camera.core.impl.utils.Threads.checkMainThread;
import static androidx.core.util.Preconditions.checkNotNull;
@@ -30,6 +31,7 @@
import android.os.Build;
import android.util.Size;
+import androidx.annotation.IntRange;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -66,7 +68,6 @@
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class StreamSharing extends UseCase {
-
@NonNull
private static final StreamSharingConfig DEFAULT_CONFIG;
@@ -89,6 +90,8 @@
MutableConfig mutableConfig = new StreamSharingBuilder().getMutableConfig();
mutableConfig.insertOption(OPTION_INPUT_FORMAT,
ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE);
+ mutableConfig.insertOption(OPTION_CAPTURE_TYPE,
+ UseCaseConfigFactory.CaptureType.STREAM_SHARING);
DEFAULT_CONFIG = new StreamSharingConfig(OptionsBundle.from(mutableConfig));
}
@@ -102,10 +105,11 @@
@NonNull UseCaseConfigFactory useCaseConfigFactory) {
super(DEFAULT_CONFIG);
mVirtualCamera = new VirtualCamera(parentCamera, children, useCaseConfigFactory,
- jpegQuality -> {
+ (jpegQuality, rotationDegrees) -> {
SurfaceProcessorNode sharingNode = mSharingNode;
if (sharingNode != null) {
- return sharingNode.getSurfaceProcessor().snapshot(jpegQuality);
+ return sharingNode.getSurfaceProcessor().snapshot(
+ jpegQuality, rotationDegrees);
} else {
return Futures.immediateFailedFuture(new Exception(
"Failed to take picture: pipeline is not ready."));
@@ -119,7 +123,7 @@
@NonNull UseCaseConfigFactory factory) {
// The shared stream optimizes for VideoCapture.
Config captureConfig = factory.getConfig(
- UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE,
+ DEFAULT_CONFIG.getCaptureType(),
ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY);
if (applyDefaultConfig) {
@@ -312,7 +316,9 @@
* Takes a snapshot of the current stream and write it to the children with JPEG Surface.
*/
@NonNull
- ListenableFuture<Void> jpegSnapshot(int jpegQuality);
+ ListenableFuture<Void> jpegSnapshot(
+ @IntRange(from = 0, to = 100) int jpegQuality,
+ @IntRange(from = 0, to = 359) int rotationDegrees);
}
@VisibleForTesting
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharingBuilder.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharingBuilder.java
index 0ae8daa..d701527 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharingBuilder.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharingBuilder.java
@@ -16,6 +16,7 @@
package androidx.camera.core.streamsharing;
+import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE;
import static androidx.camera.core.internal.TargetConfig.OPTION_TARGET_CLASS;
import static androidx.camera.core.internal.TargetConfig.OPTION_TARGET_NAME;
@@ -23,6 +24,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.UseCase;
import androidx.camera.core.impl.CaptureConfig;
@@ -31,6 +33,7 @@
import androidx.camera.core.impl.OptionsBundle;
import androidx.camera.core.impl.SessionConfig;
import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.impl.UseCaseConfigFactory;
import androidx.camera.core.internal.TargetConfig;
import java.util.UUID;
@@ -162,4 +165,13 @@
@NonNull UseCase.EventCallback eventCallback) {
throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
}
+
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @NonNull
+ @Override
+ public StreamSharingBuilder setCaptureType(
+ @NonNull UseCaseConfigFactory.CaptureType captureType) {
+ getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
+ return this;
+ }
}
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 1ca8ace..cdbd616 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
@@ -20,18 +20,14 @@
import static java.util.Collections.singletonList;
import static java.util.Objects.requireNonNull;
-import android.graphics.Rect;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
-import androidx.camera.core.FocusMeteringAction;
-import androidx.camera.core.FocusMeteringResult;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.impl.CameraControlInternal;
import androidx.camera.core.impl.CaptureConfig;
-import androidx.camera.core.impl.Config;
-import androidx.camera.core.impl.SessionConfig;
+import androidx.camera.core.impl.ForwardingCameraControl;
import androidx.camera.core.impl.utils.futures.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -42,90 +38,29 @@
* A {@link CameraControlInternal} that is used to control the virtual camera.
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
-public class VirtualCameraControl implements CameraControlInternal {
+public class VirtualCameraControl extends ForwardingCameraControl {
private static final int DEFAULT_JPEG_QUALITY = 100;
+ private static final int DEFAULT_ROTATION_DEGREES = 0;
- private final CameraControlInternal mParent;
private final StreamSharing.Control mStreamSharingControl;
VirtualCameraControl(@NonNull CameraControlInternal parent,
@NonNull StreamSharing.Control streamSharingControl) {
- mParent = parent;
+ super(parent);
mStreamSharingControl = streamSharingControl;
}
@NonNull
@Override
- public ListenableFuture<Void> enableTorch(boolean torch) {
- return mParent.enableTorch(torch);
- }
-
- @NonNull
- @Override
- public ListenableFuture<FocusMeteringResult> startFocusAndMetering(
- @NonNull FocusMeteringAction action) {
- return mParent.startFocusAndMetering(action);
- }
-
- @NonNull
- @Override
- public ListenableFuture<Void> cancelFocusAndMetering() {
- return mParent.cancelFocusAndMetering();
- }
-
- @NonNull
- @Override
- public ListenableFuture<Void> setZoomRatio(float ratio) {
- return mParent.setZoomRatio(ratio);
- }
-
- @NonNull
- @Override
- public ListenableFuture<Void> setLinearZoom(float linearZoom) {
- return mParent.setLinearZoom(linearZoom);
- }
-
- @NonNull
- @Override
- public ListenableFuture<Integer> setExposureCompensationIndex(int value) {
- return mParent.setExposureCompensationIndex(value);
- }
-
- @Override
- public int getFlashMode() {
- return mParent.getFlashMode();
- }
-
- @Override
- public void setFlashMode(int flashMode) {
- mParent.setFlashMode(flashMode);
- }
-
- @Override
- public void addZslConfig(@NonNull SessionConfig.Builder sessionConfigBuilder) {
- mParent.addZslConfig(sessionConfigBuilder);
- }
-
- @Override
- public void setZslDisabledByUserCaseConfig(boolean disabled) {
- mParent.setZslDisabledByUserCaseConfig(disabled);
- }
-
- @Override
- public boolean isZslDisabledByByUserCaseConfig() {
- return mParent.isZslDisabledByByUserCaseConfig();
- }
-
- @NonNull
- @Override
public ListenableFuture<List<Void>> submitStillCaptureRequests(
@NonNull List<CaptureConfig> captureConfigs,
@ImageCapture.CaptureMode int captureMode,
@ImageCapture.FlashType int flashType) {
checkArgument(captureConfigs.size() == 1, "Only support one capture config.");
- int jpegQuality = getJpegQuality(captureConfigs.get(0));
- return Futures.allAsList(singletonList(mStreamSharingControl.jpegSnapshot(jpegQuality)));
+ return Futures.allAsList(singletonList(mStreamSharingControl.jpegSnapshot(
+ getJpegQuality(captureConfigs.get(0)),
+ getRotationDegrees(captureConfigs.get(0)))));
}
private int getJpegQuality(@NonNull CaptureConfig captureConfig) {
@@ -133,31 +68,8 @@
CaptureConfig.OPTION_JPEG_QUALITY, DEFAULT_JPEG_QUALITY));
}
- @NonNull
- @Override
- public SessionConfig getSessionConfig() {
- return mParent.getSessionConfig();
- }
-
- @NonNull
- @Override
- public Rect getSensorRect() {
- return mParent.getSensorRect();
- }
-
- @Override
- public void addInteropConfig(@NonNull Config config) {
- mParent.addInteropConfig(config);
- }
-
- @Override
- public void clearInteropConfig() {
- mParent.clearInteropConfig();
- }
-
- @NonNull
- @Override
- public Config getInteropConfig() {
- return mParent.getInteropConfig();
+ private int getRotationDegrees(@NonNull CaptureConfig captureConfig) {
+ return requireNonNull(captureConfig.getImplementationOptions().retrieveOption(
+ CaptureConfig.OPTION_ROTATION, DEFAULT_ROTATION_DEGREES));
}
}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/FocusMeteringActionTest.java b/camera/camera-core/src/test/java/androidx/camera/core/FocusMeteringActionTest.java
index c20d759..1f7200f 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/FocusMeteringActionTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/FocusMeteringActionTest.java
@@ -23,6 +23,7 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.internal.DoNotInstrument;
+import java.util.Arrays;
import java.util.concurrent.TimeUnit;
@RunWith(RobolectricTestRunner.class)
@@ -311,13 +312,53 @@
}
@Test(expected = IllegalArgumentException.class)
- public void builderWithNullPoint() {
- new FocusMeteringAction.Builder(null).build();
- }
-
- @Test(expected = IllegalArgumentException.class)
public void builderWithNullPoint2() {
new FocusMeteringAction.Builder(null, FocusMeteringAction.FLAG_AF).build();
}
+ @Test
+ public void copyBuilder() {
+ // 1. Arrange
+ FocusMeteringAction action1 = new FocusMeteringAction.Builder(mPoint1)
+ .addPoint(mPoint2, FocusMeteringAction.FLAG_AE)
+ .setAutoCancelDuration(8000, TimeUnit.MILLISECONDS)
+ .build();
+
+ // 2. Act
+ FocusMeteringAction action2 = new FocusMeteringAction.Builder(action1).build();
+
+ // 3. Assert
+ assertThat(action1.getMeteringPointsAf()).containsExactlyElementsIn(
+ action2.getMeteringPointsAf()
+ );
+ assertThat(action1.getMeteringPointsAe()).containsExactlyElementsIn(
+ action2.getMeteringPointsAe()
+ );
+ assertThat(action1.getMeteringPointsAwb()).containsExactlyElementsIn(
+ action2.getMeteringPointsAwb()
+ );
+ assertThat(action1.getAutoCancelDurationInMillis())
+ .isEqualTo(action2.getAutoCancelDurationInMillis());
+ assertThat(action1.isAutoCancelEnabled()).isEqualTo(action2.isAutoCancelEnabled());
+ }
+
+ @Test
+ public void removePoints() {
+ // 1. Arrange
+ FocusMeteringAction.Builder builder = new FocusMeteringAction.Builder(mPoint1)
+ .addPoint(mPoint2, FocusMeteringAction.FLAG_AE);
+
+ // 2. Act
+ FocusMeteringAction action = builder.removePoints(FocusMeteringAction.FLAG_AE).build();
+
+ // 3. Assert
+ assertThat(action.getMeteringPointsAe()).isEmpty();
+ assertThat(action.getMeteringPointsAf()).containsExactlyElementsIn(
+ Arrays.asList(mPoint1)
+ );
+ assertThat(action.getMeteringPointsAwb()).containsExactlyElementsIn(
+ Arrays.asList(mPoint1)
+ );
+ }
+
}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt
index 34b1b4a..eb21888 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt
@@ -17,7 +17,6 @@
package androidx.camera.core.imagecapture
import android.graphics.ImageFormat
-import android.graphics.Matrix
import android.graphics.Rect
import android.hardware.camera2.CameraDevice
import android.os.Build
@@ -52,11 +51,9 @@
import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
import androidx.camera.core.impl.utils.futures.Futures
import androidx.camera.core.internal.IoConfig.OPTION_IO_EXECUTOR
-import androidx.camera.core.processing.Packet
import androidx.camera.testing.TestImageUtil.createJpegBytes
import androidx.camera.testing.TestImageUtil.createJpegFakeImageProxy
import androidx.camera.testing.TestImageUtil.createYuvFakeImageProxy
-import androidx.camera.testing.fakes.FakeCameraCaptureResult
import androidx.camera.testing.fakes.FakeImageInfo
import androidx.camera.testing.fakes.FakeImageReaderProxy
import androidx.camera.testing.fakes.GrayscaleImageEffect
@@ -161,37 +158,6 @@
}
@Test
- fun createPipelineWithVirtualCamera_plumbedToProcessingInput2PacketOperation() {
- // Arrange: create a pipeline with a virtual camera.
- val pipeline = ImagePipeline(
- imageCaptureConfig,
- SIZE,
- GrayscaleImageEffect(),
- true
- )
- // Listen to the input to packet operation.
- var isVirtualCamera = false
- pipeline.processingNode.injectProcessingInput2Packet {
- isVirtualCamera = it.isVirtualCamera
- return@injectProcessingInput2Packet Packet.of(
- it.imageProxy,
- null,
- it.imageProxy.cropRect,
- it.imageProxy.format,
- Matrix(),
- FakeCameraCaptureResult()
- )
- }
-
- // Act: send in-memory request.
- sendInMemoryRequest(pipeline)
-
- // Assert: the input packet is marked as from a virtual camera.
- assertThat(isVirtualCamera).isTrue()
- pipeline.close()
- }
-
- @Test
fun createRequests_verifyCameraRequest() {
// Arrange.
val captureInput = imagePipeline.captureNode.inputEdge
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt
index 096fea8..4d024c5 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt
@@ -65,7 +65,7 @@
HEIGHT
)
val processingRequest = createProcessingRequest()
- val input = ProcessingNode.InputPacket.of(processingRequest, image, false)
+ val input = ProcessingNode.InputPacket.of(processingRequest, image)
// Act.
val output = operation.apply(input)
@@ -89,7 +89,7 @@
}
val image = createJpegFakeImageProxy(jpegBytes)
val processingRequest = createProcessingRequest()
- val input = ProcessingNode.InputPacket.of(processingRequest, image, false)
+ val input = ProcessingNode.InputPacket.of(processingRequest, image)
// Act.
val output = operation.apply(input)
@@ -121,7 +121,7 @@
FakeTakePictureCallback(),
Futures.immediateFuture(null)
)
- val input = ProcessingNode.InputPacket.of(processingRequest, image, false)
+ val input = ProcessingNode.InputPacket.of(processingRequest, image)
// Act.
val output = operation.apply(input)
@@ -146,25 +146,7 @@
injectRotationOptionQuirk()
val image = createJpegFakeImageProxy(createJpegBytes(WIDTH, HEIGHT))
val processingRequest = createProcessingRequest()
- val input = ProcessingNode.InputPacket.of(processingRequest, image, false)
-
- // Act.
- val output = operation.apply(input)
-
- // Assert: the metadata are based on Packet only.
- assertThat(output.cropRect).isEqualTo(CROP_RECT)
- assertThat(output.rotationDegrees).isEqualTo(ROTATION_DEGREES)
- assertThat(output.format).isEqualTo(ImageFormat.JPEG)
- assertThat(output.size).isEqualTo(Size(WIDTH, HEIGHT))
- assertThat(output.sensorToBufferTransform).isEqualTo(SENSOR_TO_BUFFER)
- }
-
- @Test
- fun isVirtualCamera_outputIgnoresExifRotation() {
- // Arrange: create input
- val image = createJpegFakeImageProxy(createJpegBytes(WIDTH, HEIGHT))
- val processingRequest = createProcessingRequest()
- val input = ProcessingNode.InputPacket.of(processingRequest, image, true)
+ val input = ProcessingNode.InputPacket.of(processingRequest, image)
// Act.
val output = operation.apply(input)
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt
index 8710e8b..a2281b2 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt
@@ -81,7 +81,7 @@
// Act: process the request.
val jpegBytes = createJpegBytes(WIDTH, HEIGHT)
val image = createJpegFakeImageProxy(jpegBytes)
- processingNodeIn.edge.accept(ProcessingNode.InputPacket.of(request, image, false))
+ processingNodeIn.edge.accept(ProcessingNode.InputPacket.of(request, image))
shadowOf(getMainLooper()).idle()
// Assert: the image is not saved.
@@ -94,7 +94,7 @@
val takePictureCallback = FakeTakePictureCallback()
val image = FakeImageProxy(FakeImageInfo())
val processingRequest = createProcessingRequest(takePictureCallback)
- val input = ProcessingNode.InputPacket.of(processingRequest, image, false)
+ val input = ProcessingNode.InputPacket.of(processingRequest, image)
// Act: send input to the edge and wait for callback
processingNodeIn.edge.accept(input)
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt
index 94b0c42..a99697e 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt
@@ -44,7 +44,7 @@
@Before
fun setUp() {
- captureNodeOut = CaptureNode.Out.of(ImageFormat.JPEG, ImageFormat.JPEG, false)
+ captureNodeOut = CaptureNode.Out.of(ImageFormat.JPEG, ImageFormat.JPEG)
matchingNodeOut = node.transform(captureNodeOut)
matchingNodeOut.edge.setListener {
packetPropagated.add(it)
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/impl/AttachedSurfaceInfoTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/impl/AttachedSurfaceInfoTest.kt
index eab2744..8a3d600 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/impl/AttachedSurfaceInfoTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/impl/AttachedSurfaceInfoTest.kt
@@ -20,6 +20,8 @@
import android.util.Range
import android.util.Size
import androidx.camera.core.DynamicRange
+import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType
+import androidx.camera.testing.fakes.FakeUseCaseConfig
import com.google.common.truth.Truth
import org.junit.Before
import org.junit.Test
@@ -40,11 +42,23 @@
private val imageFormat = ImageFormat.JPEG
private val size = Size(1920, 1080)
private val dynamicRange = DynamicRange.SDR
+ private val captureTypes = listOf(CaptureType.PREVIEW)
+ private val inputFormat = ImageFormat.PRIVATE
private val targetFramerate = Range(10, 20)
+ private val config = FakeUseCaseConfig.Builder(
+ CaptureType.PREVIEW,
+ inputFormat
+ ).useCaseConfig.config
+
@Before
fun setup() {
attachedSurfaceInfo = AttachedSurfaceInfo.create(
- surfaceConfig, imageFormat, size, dynamicRange,
+ surfaceConfig,
+ imageFormat,
+ size,
+ dynamicRange,
+ captureTypes,
+ config,
targetFramerate
)
}
@@ -74,6 +88,28 @@
}
@Test
+ fun canGetCaptureTypes() {
+ Truth.assertThat(attachedSurfaceInfo!!.captureTypes.size).isEqualTo(captureTypes.size)
+ for ((index, value) in captureTypes.withIndex()) {
+ Truth.assertThat(attachedSurfaceInfo!!.captureTypes[index]).isEqualTo(value)
+ }
+ }
+
+ @Test
+ fun canGetImplementationOption() {
+ Truth.assertThat(
+ attachedSurfaceInfo!!.implementationOptions!!
+ .containsOption(ImageInputConfig.OPTION_INPUT_FORMAT)
+ )
+ .isTrue()
+ Truth.assertThat(
+ attachedSurfaceInfo!!.implementationOptions!!
+ .retrieveOption(ImageInputConfig.OPTION_INPUT_FORMAT)
+ )
+ .isEqualTo(inputFormat)
+ }
+
+ @Test
fun canGetTargetFrameRate() {
Truth.assertThat(attachedSurfaceInfo!!.targetFrameRate).isEqualTo(targetFramerate)
}
@@ -85,6 +121,8 @@
imageFormat,
size,
dynamicRange,
+ listOf(CaptureType.PREVIEW),
+ config,
null
)
Truth.assertThat(attachedSurfaceInfo2.targetFrameRate).isNull()
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.kt
index cabf0cc..670ef68 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.kt
@@ -20,15 +20,22 @@
import android.graphics.Matrix
import android.graphics.Rect
import android.os.Build
+import android.util.Range
import android.util.Rational
import android.util.Size
import android.view.Surface
import androidx.camera.core.CameraEffect
import androidx.camera.core.CameraEffect.PREVIEW
import androidx.camera.core.CameraEffect.VIDEO_CAPTURE
+import androidx.camera.core.FocusMeteringAction
+import androidx.camera.core.FocusMeteringAction.FLAG_AE
+import androidx.camera.core.FocusMeteringAction.FLAG_AF
+import androidx.camera.core.FocusMeteringAction.FLAG_AWB
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageCapture
import androidx.camera.core.Preview
+import androidx.camera.core.SurfaceOrientedMeteringPointFactory
+import androidx.camera.core.TorchState
import androidx.camera.core.UseCase
import androidx.camera.core.ViewPort
import androidx.camera.core.concurrent.CameraCoordinator
@@ -38,6 +45,8 @@
import androidx.camera.core.impl.Identifier
import androidx.camera.core.impl.MutableOptionsBundle
import androidx.camera.core.impl.OptionsBundle
+import androidx.camera.core.impl.RestrictedCameraControl
+import androidx.camera.core.impl.SessionProcessor
import androidx.camera.core.impl.StreamSpec
import androidx.camera.core.impl.UseCaseConfigFactory
import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
@@ -45,17 +54,23 @@
import androidx.camera.core.processing.DefaultSurfaceProcessor
import androidx.camera.core.streamsharing.StreamSharing
import androidx.camera.testing.fakes.FakeCamera
+import androidx.camera.testing.fakes.FakeCameraControl
import androidx.camera.testing.fakes.FakeCameraCoordinator
import androidx.camera.testing.fakes.FakeCameraDeviceSurfaceManager
+import androidx.camera.testing.fakes.FakeCameraInfoInternal
+import androidx.camera.testing.fakes.FakeSessionProcessor
import androidx.camera.testing.fakes.FakeSurfaceEffect
import androidx.camera.testing.fakes.FakeSurfaceProcessorInternal
import androidx.camera.testing.fakes.FakeUseCase
import androidx.camera.testing.fakes.FakeUseCaseConfig
import androidx.camera.testing.fakes.FakeUseCaseConfigFactory
import androidx.camera.testing.fakes.GrayscaleImageEffect
+import androidx.concurrent.futures.await
+import androidx.testutils.assertThrows
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
+import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -81,7 +96,6 @@
instrumentedPackages = ["androidx.camera.core"]
)
class CameraUseCaseAdapterTest {
-
private lateinit var effects: List<CameraEffect>
private lateinit var executor: ExecutorService
@@ -93,6 +107,8 @@
private lateinit var sharedEffect: FakeSurfaceEffect
private lateinit var cameraCoordinator: CameraCoordinator
private lateinit var surfaceProcessorInternal: FakeSurfaceProcessorInternal
+ private lateinit var fakeCameraControl: FakeCameraControl
+ private lateinit var fakeCameraInfo: FakeCameraInfoInternal
private val fakeCameraSet = LinkedHashSet<CameraInternal>()
private val imageEffect = GrayscaleImageEffect()
private val preview = Preview.Builder().build()
@@ -106,7 +122,9 @@
@Before
fun setUp() {
fakeCameraDeviceSurfaceManager = FakeCameraDeviceSurfaceManager()
- fakeCamera = FakeCamera(CAMERA_ID)
+ fakeCameraControl = FakeCameraControl()
+ fakeCameraInfo = FakeCameraInfoInternal()
+ fakeCamera = FakeCamera(CAMERA_ID, fakeCameraControl, fakeCameraInfo)
cameraCoordinator = FakeCameraCoordinator()
useCaseConfigFactory = FakeUseCaseConfigFactory()
fakeCameraSet.add(fakeCamera)
@@ -209,7 +227,7 @@
fun invalidUseCaseComboCantBeFixedByStreamSharing_throwsException() {
// Arrange: create a camera that only support one JPEG stream.
fakeCameraDeviceSurfaceManager.setValidSurfaceCombos(setOf(listOf(JPEG)))
- // Act: add PRIV and JPEG streams.
+ // Act: add PRIVATE and JPEG streams.
adapter.addUseCases(setOf(preview, image))
}
@@ -909,6 +927,316 @@
assertThat(video.effect).isNull()
}
+ private fun createAdapterWithSupportedCameraOperations(
+ @RestrictedCameraControl.CameraOperation supportedOps: Set<Int>
+ ): CameraUseCaseAdapter {
+ val cameraUseCaseAdapter = CameraUseCaseAdapter(
+ fakeCameraSet,
+ cameraCoordinator,
+ fakeCameraDeviceSurfaceManager,
+ useCaseConfigFactory
+ )
+
+ val fakeSessionProcessor = FakeSessionProcessor()
+ // no camera operations are supported.
+ fakeSessionProcessor.restrictedCameraOperations = supportedOps
+ val cameraConfig: CameraConfig = FakeCameraConfig(fakeSessionProcessor)
+ cameraUseCaseAdapter.setExtendedConfig(cameraConfig)
+ return cameraUseCaseAdapter
+ }
+
+ @Test
+ fun cameraControlFailed_whenNoCameraOperationsSupported(): Unit = runBlocking {
+ // 1. Arrange
+ val cameraUseCaseAdapter =
+ createAdapterWithSupportedCameraOperations(supportedOps = emptySet())
+
+ // 2. Act && Assert
+ assertThrows<IllegalStateException> {
+ cameraUseCaseAdapter.cameraControl.setZoomRatio(1.0f).await()
+ }
+ assertThrows<IllegalStateException> {
+ cameraUseCaseAdapter.cameraControl.setLinearZoom(1.0f).await()
+ }
+ assertThrows<IllegalStateException> {
+ cameraUseCaseAdapter.cameraControl.enableTorch(true).await()
+ }
+ assertThrows<IllegalStateException> {
+ cameraUseCaseAdapter.cameraControl.startFocusAndMetering(
+ getFocusMeteringAction()
+ ).await()
+ }
+ assertThrows<IllegalStateException> {
+ cameraUseCaseAdapter.cameraControl.setExposureCompensationIndex(0).await()
+ }
+ }
+
+ private fun getFocusMeteringAction(
+ meteringMode: Int = FLAG_AF or FLAG_AE or FLAG_AWB
+ ): FocusMeteringAction {
+ val pointFactory = SurfaceOrientedMeteringPointFactory(1f, 1f)
+ return FocusMeteringAction.Builder(
+ pointFactory.createPoint(0.5f, 0.5f), meteringMode)
+ .build()
+ }
+
+ @Test
+ fun zoomEnabled_whenZoomOperationsSupported(): Unit = runBlocking {
+ // 1. Arrange
+ val cameraUseCaseAdapter =
+ createAdapterWithSupportedCameraOperations(
+ supportedOps = setOf(RestrictedCameraControl.ZOOM))
+
+ // 2. Act && Assert
+ cameraUseCaseAdapter.cameraControl.setZoomRatio(2.0f).await()
+ assertThat(fakeCameraControl.zoomRatio).isEqualTo(2.0f)
+ cameraUseCaseAdapter.cameraControl.setLinearZoom(1.0f).await()
+ assertThat(fakeCameraControl.linearZoom).isEqualTo(1.0f)
+ }
+
+ @Test
+ fun torchEnabled_whenTorchOperationSupported(): Unit = runBlocking {
+ // 1. Arrange
+ val cameraUseCaseAdapter =
+ createAdapterWithSupportedCameraOperations(
+ supportedOps = setOf(RestrictedCameraControl.TORCH))
+
+ // 2. Act
+ cameraUseCaseAdapter.cameraControl.enableTorch(true).await()
+
+ // 3. Assert
+ assertThat(fakeCameraControl.torchEnabled).isEqualTo(true)
+ }
+
+ @Test
+ fun focusMetering_afEnabled_whenAfOperationSupported(): Unit = runBlocking {
+ // 1. Arrange
+ val cameraUseCaseAdapter =
+ createAdapterWithSupportedCameraOperations(
+ supportedOps = setOf(
+ RestrictedCameraControl.AUTO_FOCUS,
+ RestrictedCameraControl.AF_REGION,
+ ))
+
+ // 2. Act
+ cameraUseCaseAdapter.cameraControl.startFocusAndMetering(
+ getFocusMeteringAction()
+ ).await()
+
+ // 3. Assert
+ // Only AF point remains, AE/AWB points removed.
+ assertThat(fakeCameraControl.lastSubmittedFocusMeteringAction?.meteringPointsAf?.size)
+ .isEqualTo(1)
+ assertThat(fakeCameraControl.lastSubmittedFocusMeteringAction?.meteringPointsAe)
+ .isEmpty()
+ assertThat(fakeCameraControl.lastSubmittedFocusMeteringAction?.meteringPointsAwb)
+ .isEmpty()
+ }
+
+ @Test
+ fun focusMetering_aeEnabled_whenAeOperationsSupported(): Unit = runBlocking {
+ // 1. Arrange
+ val cameraUseCaseAdapter =
+ createAdapterWithSupportedCameraOperations(
+ supportedOps = setOf(
+ RestrictedCameraControl.AE_REGION,
+ ))
+
+ // 2. Act
+ cameraUseCaseAdapter.cameraControl.startFocusAndMetering(
+ getFocusMeteringAction()
+ ).await()
+
+ // 3. Assert
+ // Only AE point remains, AF/AWB points removed.
+ assertThat(fakeCameraControl.lastSubmittedFocusMeteringAction?.meteringPointsAe?.size)
+ .isEqualTo(1)
+ assertThat(fakeCameraControl.lastSubmittedFocusMeteringAction?.meteringPointsAf)
+ .isEmpty()
+ assertThat(fakeCameraControl.lastSubmittedFocusMeteringAction?.meteringPointsAwb)
+ .isEmpty()
+ }
+
+ @Test
+ fun focusMetering_awbEnabled_whenAwbOperationsSupported(): Unit = runBlocking {
+ // 1. Arrange
+ val cameraUseCaseAdapter =
+ createAdapterWithSupportedCameraOperations(
+ supportedOps = setOf(
+ RestrictedCameraControl.AWB_REGION,
+ ))
+
+ // 2. Act
+ cameraUseCaseAdapter.cameraControl.startFocusAndMetering(
+ getFocusMeteringAction()
+ ).await()
+
+ // 3. Assert
+ // Only AWB point remains, AF/AE points removed.
+ assertThat(fakeCameraControl.lastSubmittedFocusMeteringAction?.meteringPointsAwb?.size)
+ .isEqualTo(1)
+ assertThat(fakeCameraControl.lastSubmittedFocusMeteringAction?.meteringPointsAf)
+ .isEmpty()
+ assertThat(fakeCameraControl.lastSubmittedFocusMeteringAction?.meteringPointsAe)
+ .isEmpty()
+ }
+
+ @Test
+ fun focusMetering_disabled_whenNoneIsSupported(): Unit = runBlocking {
+ // 1. Arrange
+ val cameraUseCaseAdapter =
+ createAdapterWithSupportedCameraOperations(
+ supportedOps = setOf(
+ RestrictedCameraControl.AE_REGION,
+ ))
+
+ // 2. Act && Assert
+ assertThrows<IllegalStateException> {
+ cameraUseCaseAdapter.cameraControl.startFocusAndMetering(
+ getFocusMeteringAction(FLAG_AF or FLAG_AWB)
+ ).await()
+ }
+ assertThat(fakeCameraControl.lastSubmittedFocusMeteringAction).isNull()
+ }
+
+ @Test
+ fun exposureEnabled_whenExposureOperationSupported(): Unit = runBlocking {
+ // 1. Arrange
+ val cameraUseCaseAdapter =
+ createAdapterWithSupportedCameraOperations(
+ supportedOps = setOf(RestrictedCameraControl.EXPOSURE_COMPENSATION))
+
+ // 2. Act
+ cameraUseCaseAdapter.cameraControl.setExposureCompensationIndex(0).await()
+
+ // 3. Assert
+ assertThat(fakeCameraControl.exposureCompensationIndex).isEqualTo(0)
+ }
+
+ @Test
+ fun cameraInfo_returnsDisabledState_AllOpsDisabled(): Unit = runBlocking {
+ // 1. Arrange
+ val cameraUseCaseAdapter =
+ createAdapterWithSupportedCameraOperations(
+ supportedOps = emptySet())
+
+ // 2. Act && Assert
+ // Zoom is disabled
+ val zoomState = cameraUseCaseAdapter.cameraInfo.zoomState.value!!
+ assertThat(zoomState.minZoomRatio).isEqualTo(1f)
+ assertThat(zoomState.maxZoomRatio).isEqualTo(1f)
+ assertThat(zoomState.zoomRatio).isEqualTo(1f)
+ assertThat(zoomState.linearZoom).isEqualTo(0f)
+
+ // Flash is disabled
+ assertThat(cameraUseCaseAdapter.cameraInfo.hasFlashUnit()).isFalse()
+
+ // Torch is disabled.
+ assertThat(cameraUseCaseAdapter.cameraInfo.torchState.value).isEqualTo(TorchState.OFF)
+
+ // FocusMetering is disabled.
+ assertThat(cameraUseCaseAdapter.cameraInfo
+ .isFocusMeteringSupported(getFocusMeteringAction()))
+ .isFalse()
+
+ // ExposureCompensation is disabled.
+ assertThat(cameraUseCaseAdapter.cameraInfo.exposureState.isExposureCompensationSupported)
+ .isFalse()
+ assertThat(cameraUseCaseAdapter.cameraInfo.exposureState.exposureCompensationRange)
+ .isEqualTo(Range(0, 0))
+ assertThat(cameraUseCaseAdapter.cameraInfo.exposureState.exposureCompensationStep)
+ .isEqualTo(Rational.ZERO)
+ assertThat(cameraUseCaseAdapter.cameraInfo.exposureState.exposureCompensationIndex)
+ .isEqualTo(0)
+ }
+
+ @Test
+ fun cameraInfo_zoomEnabled(): Unit = runBlocking {
+ // 1. Arrange
+ val cameraUseCaseAdapter =
+ createAdapterWithSupportedCameraOperations(
+ supportedOps = setOf(RestrictedCameraControl.ZOOM)
+ )
+ fakeCameraInfo.setZoom(10f, 0.6f, 10f, 1f)
+
+ // 2. Act
+ val zoomState = cameraUseCaseAdapter.cameraInfo.zoomState.value!!
+
+ // 3. Assert
+ val fakeZoomState = fakeCameraInfo.zoomState.value!!
+ assertThat(zoomState.zoomRatio).isEqualTo(fakeZoomState.zoomRatio)
+ assertThat(zoomState.minZoomRatio).isEqualTo(fakeZoomState.minZoomRatio)
+ assertThat(zoomState.maxZoomRatio).isEqualTo(fakeZoomState.maxZoomRatio)
+ assertThat(zoomState.linearZoom).isEqualTo(fakeZoomState.linearZoom)
+ }
+
+ @Test
+ fun cameraInfo_torchEnabled(): Unit = runBlocking {
+ // 1. Arrange
+ val cameraUseCaseAdapter =
+ createAdapterWithSupportedCameraOperations(
+ supportedOps = setOf(RestrictedCameraControl.TORCH)
+ )
+ fakeCameraInfo.setTorch(TorchState.ON)
+
+ // 2. Act && Assert
+ assertThat(cameraUseCaseAdapter.cameraInfo.torchState.value)
+ .isEqualTo(fakeCameraInfo.torchState.value)
+ }
+
+ @Test
+ fun cameraInfo_afEnabled(): Unit = runBlocking {
+ // 1. Arrange
+ val cameraUseCaseAdapter =
+ createAdapterWithSupportedCameraOperations(
+ supportedOps = setOf(
+ RestrictedCameraControl.AUTO_FOCUS,
+ RestrictedCameraControl.AF_REGION
+ )
+ )
+ fakeCameraInfo.setIsFocusMeteringSupported(true)
+
+ // 2. Act && Assert
+ assertThat(cameraUseCaseAdapter.cameraInfo.isFocusMeteringSupported(
+ getFocusMeteringAction()
+ )).isTrue()
+ }
+
+ @Test
+ fun cameraInfo_exposureExposureEnabled(): Unit = runBlocking {
+ // 1. Arrange
+ val cameraUseCaseAdapter =
+ createAdapterWithSupportedCameraOperations(
+ supportedOps = setOf(
+ RestrictedCameraControl.EXPOSURE_COMPENSATION,
+ )
+ )
+ fakeCameraInfo.setExposureState(2, Range.create(0, 10), Rational(1, 1), true)
+
+ // 2. Act && Assert
+ assertThat(cameraUseCaseAdapter.cameraInfo.exposureState.exposureCompensationIndex)
+ .isEqualTo(fakeCameraInfo.exposureState.exposureCompensationIndex)
+ assertThat(cameraUseCaseAdapter.cameraInfo.exposureState.exposureCompensationRange)
+ .isEqualTo(fakeCameraInfo.exposureState.exposureCompensationRange)
+ assertThat(cameraUseCaseAdapter.cameraInfo.exposureState.exposureCompensationStep)
+ .isEqualTo(fakeCameraInfo.exposureState.exposureCompensationStep)
+ assertThat(cameraUseCaseAdapter.cameraInfo.exposureState.isExposureCompensationSupported)
+ .isEqualTo(fakeCameraInfo.exposureState.isExposureCompensationSupported)
+ }
+
+ @Test
+ fun cameraInfo_flashEnabled(): Unit = runBlocking {
+ // 1. Arrange
+ val cameraUseCaseAdapter =
+ createAdapterWithSupportedCameraOperations(
+ supportedOps = setOf(RestrictedCameraControl.FLASH)
+ )
+
+ // 2. Act && Assert
+ assertThat(cameraUseCaseAdapter.cameraInfo.hasFlashUnit())
+ .isEqualTo(fakeCameraInfo.hasFlashUnit())
+ }
+
private fun createCoexistingRequiredRuleCameraConfig(): CameraConfig {
return object : CameraConfig {
private val mUseCaseConfigFactory =
@@ -950,7 +1278,9 @@
return false
}
- private class FakeCameraConfig : CameraConfig {
+ private class FakeCameraConfig(
+ val sessionProcessor: FakeSessionProcessor? = null
+ ) : CameraConfig {
private val mUseCaseConfigFactory =
UseCaseConfigFactory { _, _ -> null }
private val mIdentifier = Identifier.create(Any())
@@ -965,5 +1295,13 @@
override fun getConfig(): Config {
return OptionsBundle.emptyBundle()
}
+
+ override fun getSessionProcessor(valueIfMissing: SessionProcessor?): SessionProcessor? {
+ return sessionProcessor ?: valueIfMissing
+ }
+
+ override fun getSessionProcessor(): SessionProcessor {
+ return sessionProcessor!!
+ }
}
}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/StreamSharingTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/StreamSharingTest.kt
index 571a6b7..969f695 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/StreamSharingTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/StreamSharingTest.kt
@@ -19,6 +19,7 @@
import android.os.Build
import android.os.Looper.getMainLooper
import android.util.Size
+import android.view.Surface
import androidx.camera.core.CameraEffect
import androidx.camera.core.CameraEffect.IMAGE_CAPTURE
import androidx.camera.core.CameraEffect.PREVIEW
@@ -102,8 +103,10 @@
@Test
fun childTakingPicture_getJpegQuality() {
// Arrange: set up StreamSharing with min latency ImageCapture as child
- val imageCapture =
- ImageCapture.Builder().setCaptureMode(CAPTURE_MODE_MINIMIZE_LATENCY).build()
+ val imageCapture = ImageCapture.Builder()
+ .setTargetRotation(Surface.ROTATION_90)
+ .setCaptureMode(CAPTURE_MODE_MINIMIZE_LATENCY)
+ .build()
streamSharing = StreamSharing(camera, setOf(child1, imageCapture), useCaseConfigFactory)
streamSharing.bindToCamera(camera, null, defaultConfig)
streamSharing.onSuggestedStreamSpecUpdated(StreamSpec.builder(size).build())
@@ -116,6 +119,7 @@
// Assert: the jpeg quality of min latency capture is 95.
assertThat(sharingProcessor.jpegQuality).isEqualTo(95)
+ assertThat(sharingProcessor.rotationDegrees).isEqualTo(270)
}
@Test
@@ -297,7 +301,7 @@
val config = streamSharing.getDefaultConfig(true, useCaseConfigFactory)!!
assertThat(useCaseConfigFactory.lastRequestedCaptureType)
- .isEqualTo(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE)
+ .isEqualTo(UseCaseConfigFactory.CaptureType.STREAM_SHARING)
assertThat(
config.retrieveOption(
OPTION_TARGET_CLASS,
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraTest.kt
index 905f660..e6062de 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraTest.kt
@@ -97,7 +97,7 @@
fun setUp() {
virtualCamera = VirtualCamera(
parentCamera, setOf(child1, child2), useCaseConfigFactory
- ) {
+ ) { _, _ ->
snapshotTriggered = true
Futures.immediateFuture(null)
}
@@ -255,7 +255,7 @@
val imageCapture = ImageCapture.Builder().build()
virtualCamera = VirtualCamera(
parentCamera, setOf(preview, child2, imageCapture), useCaseConfigFactory
- ) {
+ ) { _, _ ->
Futures.immediateFuture(null)
}
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/AdvancedSessionProcessorTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/AdvancedSessionProcessorTest.kt
index 0e76994..cfa06c8 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/AdvancedSessionProcessorTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/AdvancedSessionProcessorTest.kt
@@ -204,7 +204,8 @@
@Test
fun canInvokeStartTrigger() = runBlocking {
val fakeSessionProcessImpl = FakeSessionProcessImpl()
- val advancedSessionProcessor = AdvancedSessionProcessor(fakeSessionProcessImpl, context)
+ val advancedSessionProcessor = AdvancedSessionProcessor(
+ fakeSessionProcessImpl, emptyList(), context)
val parametersMap: MutableMap<CaptureRequest.Key<*>, Any> = mutableMapOf(
CaptureRequest.CONTROL_AF_MODE to CaptureRequest.CONTROL_AF_MODE_AUTO,
@@ -375,7 +376,8 @@
imageCapture: ImageCapture,
imageAnalysis: ImageAnalysis? = null
) {
- val advancedSessionProcessor = AdvancedSessionProcessor(fakeSessionProcessImpl, context)
+ val advancedSessionProcessor = AdvancedSessionProcessor(fakeSessionProcessImpl,
+ emptyList(), context)
val latchPreviewFrame = CountDownLatch(1)
val latchAnalysis = CountDownLatch(1)
val deferCapturedImage = CompletableDeferred<ImageProxy>()
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt
index f9e1b5a..5a0fb78 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt
@@ -154,7 +154,7 @@
fakePreviewExtenderImpl = FakePreviewExtenderImpl(previewProcessorType)
fakeCaptureExtenderImpl = FakeImageCaptureExtenderImpl(hasCaptureProcessor)
basicExtenderSessionProcessor = BasicExtenderSessionProcessor(
- fakePreviewExtenderImpl, fakeCaptureExtenderImpl, context
+ fakePreviewExtenderImpl, fakeCaptureExtenderImpl, emptyList(), context
)
}
@@ -180,6 +180,10 @@
cameraProvider.shutdown()[10, TimeUnit.SECONDS]
}
}
+
+ if (::basicExtenderSessionProcessor.isInitialized) {
+ basicExtenderSessionProcessor.deInitSession()
+ }
}
@Test
@@ -205,7 +209,7 @@
hasCaptureProcessor, throwErrorOnProcess = true
)
basicExtenderSessionProcessor = BasicExtenderSessionProcessor(
- fakePreviewExtenderImpl, fakeCaptureExtenderImpl, context
+ fakePreviewExtenderImpl, fakeCaptureExtenderImpl, emptyList(), context
)
val preview = Preview.Builder().build()
val imageCapture = ImageCapture.Builder().build()
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java
index 17a7a78..5722a6b 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
import android.util.Pair;
import android.util.Range;
import android.util.Size;
@@ -27,9 +28,11 @@
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
+import androidx.annotation.VisibleForTesting;
import androidx.camera.camera2.interop.Camera2CameraInfo;
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.CameraInfo;
+import androidx.camera.core.Logger;
import androidx.camera.core.impl.SessionProcessor;
import androidx.camera.extensions.ExtensionMode;
import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl;
@@ -52,6 +55,7 @@
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public class AdvancedVendorExtender implements VendorExtender {
+ private static final String TAG = "AdvancedVendorExtender";
private final ExtensionDisabledValidator mExtensionDisabledValidator =
new ExtensionDisabledValidator();
private final AdvancedExtenderImpl mAdvancedExtenderImpl;
@@ -84,6 +88,11 @@
}
}
+ @VisibleForTesting
+ AdvancedVendorExtender(AdvancedExtenderImpl advancedExtenderImpl) {
+ mAdvancedExtenderImpl = advancedExtenderImpl;
+ }
+
@OptIn(markerClass = ExperimentalCamera2Interop.class)
@Override
public void init(@NonNull CameraInfo cameraInfo) {
@@ -151,11 +160,28 @@
return yuvList == null ? new Size[0] : yuvList.toArray(new Size[0]);
}
+ @NonNull
+ private List<CaptureRequest.Key> getSupportedParameterKeys() {
+ List<CaptureRequest.Key> keys = Collections.emptyList();
+ if (ExtensionVersion.getRuntimeVersion().compareTo(Version.VERSION_1_3) >= 0) {
+ try {
+ keys = Collections.unmodifiableList(
+ mAdvancedExtenderImpl.getAvailableCaptureRequestKeys());
+ } catch (Exception e) {
+ Logger.e(TAG, "AdvancedExtenderImpl.getAvailableCaptureRequestKeys "
+ + "throws exceptions", e);
+ }
+ }
+ return keys;
+ }
+
@Nullable
@Override
public SessionProcessor createSessionProcessor(@NonNull Context context) {
Preconditions.checkNotNull(mCameraId, "VendorExtender#init() must be called first");
return new AdvancedSessionProcessor(
- mAdvancedExtenderImpl.createSessionProcessor(), context);
+ mAdvancedExtenderImpl.createSessionProcessor(),
+ getSupportedParameterKeys(),
+ context);
}
}
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
index 2a7ed76..61f0550 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
@@ -19,7 +19,9 @@
import android.content.Context;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
+import android.os.Build;
import android.util.Pair;
import android.util.Range;
import android.util.Size;
@@ -28,6 +30,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
+import androidx.annotation.VisibleForTesting;
import androidx.camera.camera2.interop.Camera2CameraInfo;
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.CameraInfo;
@@ -46,14 +49,14 @@
import androidx.camera.extensions.impl.NightImageCaptureExtenderImpl;
import androidx.camera.extensions.impl.NightPreviewExtenderImpl;
import androidx.camera.extensions.impl.PreviewExtenderImpl;
+import androidx.camera.extensions.internal.compat.workaround.AvailableKeysRetriever;
import androidx.camera.extensions.internal.compat.workaround.ExtensionDisabledValidator;
import androidx.camera.extensions.internal.sessionprocessor.BasicExtenderSessionProcessor;
import androidx.core.util.Preconditions;
-import org.jetbrains.annotations.TestOnly;
-
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -69,6 +72,27 @@
private PreviewExtenderImpl mPreviewExtenderImpl = null;
private ImageCaptureExtenderImpl mImageCaptureExtenderImpl = null;
private CameraInfo mCameraInfo;
+ private String mCameraId;
+ private CameraCharacteristics mCameraCharacteristics;
+ private AvailableKeysRetriever mAvailableKeysRetriever = new AvailableKeysRetriever();
+
+ static final List<CaptureRequest.Key> sBaseSupportedKeys = new ArrayList<>(Arrays.asList(
+ CaptureRequest.SCALER_CROP_REGION,
+ CaptureRequest.CONTROL_AF_MODE,
+ CaptureRequest.CONTROL_AF_TRIGGER,
+ CaptureRequest.CONTROL_AF_REGIONS,
+ CaptureRequest.CONTROL_AE_REGIONS,
+ CaptureRequest.CONTROL_AWB_REGIONS,
+ CaptureRequest.CONTROL_AE_MODE,
+ CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
+ CaptureRequest.FLASH_MODE,
+ CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION
+ ));
+ static {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ sBaseSupportedKeys.add(CaptureRequest.CONTROL_ZOOM_RATIO);
+ }
+ }
public BasicVendorExtender(@ExtensionMode.Mode int mode) {
try {
@@ -102,7 +126,7 @@
}
}
- @TestOnly
+ @VisibleForTesting
BasicVendorExtender(ImageCaptureExtenderImpl imageCaptureExtenderImpl,
PreviewExtenderImpl previewExtenderImpl) {
mPreviewExtenderImpl = previewExtenderImpl;
@@ -136,11 +160,11 @@
return;
}
- String cameraId = Camera2CameraInfo.from(cameraInfo).getCameraId();
- CameraCharacteristics cameraCharacteristics =
+ mCameraId = Camera2CameraInfo.from(cameraInfo).getCameraId();
+ mCameraCharacteristics =
Camera2CameraInfo.extractCameraCharacteristics(cameraInfo);
- mPreviewExtenderImpl.init(cameraId, cameraCharacteristics);
- mImageCaptureExtenderImpl.init(cameraId, cameraCharacteristics);
+ mPreviewExtenderImpl.init(mCameraId, mCameraCharacteristics);
+ mImageCaptureExtenderImpl.init(mCameraId, mCameraCharacteristics);
Logger.d(TAG, "PreviewExtender processorType= " + mPreviewExtenderImpl.getProcessorType());
Logger.d(TAG, "ImageCaptureExtender processor= "
@@ -285,11 +309,41 @@
return getOutputSizes(ImageFormat.YUV_420_888);
}
+ @NonNull
+ private List<CaptureRequest.Key> getSupportedParameterKeys(Context context) {
+ if (ExtensionVersion.getRuntimeVersion().compareTo(Version.VERSION_1_3) >= 0) {
+ try {
+ List<CaptureRequest.Key> keys =
+ Collections.unmodifiableList(
+ mAvailableKeysRetriever.getAvailableCaptureRequestKeys(
+ mImageCaptureExtenderImpl,
+ mCameraId,
+ mCameraCharacteristics,
+ context));
+ if (keys == null) {
+ keys = Collections.emptyList();
+ }
+ return keys;
+ } catch (Exception e) {
+ // it could crash on some OEMs.
+ Logger.e(TAG, "ImageCaptureExtenderImpl.getAvailableCaptureRequestKeys "
+ + "throws exceptions", e);
+ return Collections.emptyList();
+ }
+ } else {
+ // For Basic Extender implementing v1.2 or below, we assume zoom/tap-to-focus/flash/EC
+ // are supported for compatibility reason.
+ return Collections.unmodifiableList(sBaseSupportedKeys);
+ }
+ }
+
@Nullable
@Override
public SessionProcessor createSessionProcessor(@NonNull Context context) {
Preconditions.checkNotNull(mCameraInfo, "VendorExtender#init() must be called first");
- return new BasicExtenderSessionProcessor(mPreviewExtenderImpl, mImageCaptureExtenderImpl,
+ return new BasicExtenderSessionProcessor(
+ mPreviewExtenderImpl, mImageCaptureExtenderImpl,
+ getSupportedParameterKeys(context),
context);
}
}
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionVersion.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionVersion.java
index ec3f327..52af0b8 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionVersion.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionVersion.java
@@ -22,6 +22,8 @@
import androidx.camera.core.Logger;
import androidx.camera.extensions.impl.ExtensionVersionImpl;
+import org.jetbrains.annotations.TestOnly;
+
/**
* Provides interfaces to check the extension version.
*/
@@ -31,6 +33,15 @@
private static volatile ExtensionVersion sExtensionVersion;
+ /**
+ * For testing only. Inject a fake {@link ExtensionVersion}. Set it to {@code null} to unset
+ * it.
+ */
+ @TestOnly
+ public static void injectInstance(@Nullable ExtensionVersion extensionVersion) {
+ sExtensionVersion = extensionVersion;
+ }
+
private static ExtensionVersion getInstance() {
if (sExtensionVersion != null) {
return sExtensionVersion;
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/DeviceQuirksLoader.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/DeviceQuirksLoader.java
index 82bb2e7..532e1e8 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/DeviceQuirksLoader.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/DeviceQuirksLoader.java
@@ -48,6 +48,10 @@
quirks.add(new CrashWhenOnDisableTooSoon());
}
+ if (GetAvailableKeysNeedsOnInit.load()) {
+ quirks.add(new GetAvailableKeysNeedsOnInit());
+ }
+
return quirks;
}
}
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/GetAvailableKeysNeedsOnInit.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/GetAvailableKeysNeedsOnInit.java
new file mode 100644
index 0000000..57f0e76
--- /dev/null
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/GetAvailableKeysNeedsOnInit.java
@@ -0,0 +1,38 @@
+/*
+ * 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.extensions.internal.compat.quirk;
+
+import android.os.Build;
+
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.impl.Quirk;
+
+/**
+ * <p>QuirkSummary
+ * Bug Id: b/279541627,
+ * Description: ImageCaptureExtenderImpl.getAvailableCaptureRequestKeys and
+ * getAvailableCaptureResultKeys incorrectly expect onInit() to be invoked to supply the
+ * CameraCharacteristics. It causes a {@link NullPointerException} if onInit() is not invoked in
+ * prior to getAvailableCaptureRequestKeys or getAvailableCaptureResultKeys.
+ * Device(s): All Samsung devices
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class GetAvailableKeysNeedsOnInit implements Quirk {
+ static boolean load() {
+ return Build.BRAND.equalsIgnoreCase("SAMSUNG");
+ }
+}
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/AvailableKeysRetriever.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/AvailableKeysRetriever.java
new file mode 100644
index 0000000..605719e
--- /dev/null
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/AvailableKeysRetriever.java
@@ -0,0 +1,68 @@
+/*
+ * 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.extensions.internal.compat.workaround;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.extensions.impl.ImageCaptureExtenderImpl;
+import androidx.camera.extensions.internal.compat.quirk.DeviceQuirks;
+import androidx.camera.extensions.internal.compat.quirk.GetAvailableKeysNeedsOnInit;
+
+import java.util.List;
+
+/**
+ * A workaround for getting the available CaptureRequest keys safely.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class AvailableKeysRetriever {
+ boolean mShouldInvokeOnInit;
+
+ /**
+ * Default constructor.
+ */
+ public AvailableKeysRetriever() {
+ mShouldInvokeOnInit = DeviceQuirks.get(GetAvailableKeysNeedsOnInit.class) != null;
+ }
+
+ /**
+ * Get available CaptureRequest keys from the given {@link ImageCaptureExtenderImpl}. The
+ * cameraId, cameraCharacteristics and the context is needed for invoking onInit whenever
+ * necessary.
+ */
+ @NonNull
+ public List<CaptureRequest.Key> getAvailableCaptureRequestKeys(
+ @NonNull ImageCaptureExtenderImpl imageCaptureExtender,
+ @NonNull String cameraId,
+ @NonNull CameraCharacteristics cameraCharacteristics,
+ @NonNull Context context) {
+ if (mShouldInvokeOnInit) {
+ imageCaptureExtender.onInit(cameraId, cameraCharacteristics, context);
+ }
+
+ try {
+ return imageCaptureExtender.getAvailableCaptureRequestKeys();
+ } finally {
+ if (mShouldInvokeOnInit) {
+ imageCaptureExtender.onDeInit();
+ }
+ }
+ }
+}
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/AdvancedSessionProcessor.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/AdvancedSessionProcessor.java
index ac7b358..ab42134 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/AdvancedSessionProcessor.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/AdvancedSessionProcessor.java
@@ -62,7 +62,10 @@
private final SessionProcessorImpl mImpl;
private final Context mContext;
- public AdvancedSessionProcessor(@NonNull SessionProcessorImpl impl, @NonNull Context context) {
+ public AdvancedSessionProcessor(@NonNull SessionProcessorImpl impl,
+ @NonNull List<CaptureRequest.Key> supportedKeys,
+ @NonNull Context context) {
+ super(supportedKeys);
mImpl = impl;
mContext = context;
}
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessor.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessor.java
index 5452a8a..4a282ab 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessor.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessor.java
@@ -96,7 +96,9 @@
public BasicExtenderSessionProcessor(@NonNull PreviewExtenderImpl previewExtenderImpl,
@NonNull ImageCaptureExtenderImpl imageCaptureExtenderImpl,
+ @NonNull List<CaptureRequest.Key> supportedKeys,
@NonNull Context context) {
+ super(supportedKeys);
mPreviewExtenderImpl = previewExtenderImpl;
mImageCaptureExtenderImpl = imageCaptureExtenderImpl;
mContext = context;
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/SessionProcessorBase.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/SessionProcessorBase.java
index a10ddcb..f02f5fa 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/SessionProcessorBase.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/SessionProcessorBase.java
@@ -20,6 +20,7 @@
import android.hardware.camera2.CaptureRequest;
import android.media.Image;
import android.media.ImageReader;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
@@ -36,15 +37,20 @@
import androidx.camera.core.Logger;
import androidx.camera.core.impl.DeferrableSurface;
import androidx.camera.core.impl.OutputSurface;
+import androidx.camera.core.impl.RestrictedCameraControl;
+import androidx.camera.core.impl.RestrictedCameraControl.CameraOperation;
import androidx.camera.core.impl.SessionConfig;
import androidx.camera.core.impl.SessionProcessor;
import androidx.camera.core.impl.SessionProcessorSurface;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Base class for SessionProcessor implementation. It is responsible for creating image readers and
@@ -67,6 +73,67 @@
private String mCameraId;
@NonNull
+
+ private final @CameraOperation Set<Integer> mSupportedCameraOperations;
+
+ SessionProcessorBase(@NonNull List<CaptureRequest.Key> supportedParameterKeys) {
+ mSupportedCameraOperations = getSupportedCameraOperations(supportedParameterKeys);
+ }
+
+ private @CameraOperation Set<Integer> getSupportedCameraOperations(
+ @NonNull List<CaptureRequest.Key> supportedParameterKeys) {
+ @CameraOperation Set<Integer> operations = new HashSet<>();
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ if (supportedParameterKeys.contains(CaptureRequest.CONTROL_ZOOM_RATIO)
+ || supportedParameterKeys.contains(CaptureRequest.SCALER_CROP_REGION)) {
+ operations.add(RestrictedCameraControl.ZOOM);
+ }
+ } else {
+ if (supportedParameterKeys.contains(CaptureRequest.SCALER_CROP_REGION)) {
+ operations.add(RestrictedCameraControl.ZOOM);
+ }
+ }
+
+ if (supportedParameterKeys.containsAll(
+ Arrays.asList(
+ CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_MODE))) {
+ operations.add(RestrictedCameraControl.AUTO_FOCUS);
+ }
+
+ if (supportedParameterKeys.contains(CaptureRequest.CONTROL_AF_REGIONS)) {
+ operations.add(RestrictedCameraControl.AF_REGION);
+ }
+
+ if (supportedParameterKeys.contains(CaptureRequest.CONTROL_AE_REGIONS)) {
+ operations.add(RestrictedCameraControl.AE_REGION);
+ }
+
+ if (supportedParameterKeys.contains(CaptureRequest.CONTROL_AWB_REGIONS)) {
+ operations.add(RestrictedCameraControl.AWB_REGION);
+ }
+
+ if (supportedParameterKeys.containsAll(
+ Arrays.asList(
+ CaptureRequest.CONTROL_AE_MODE,
+ CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER))) {
+ operations.add(RestrictedCameraControl.FLASH);
+ }
+
+ if (supportedParameterKeys.containsAll(
+ Arrays.asList(
+ CaptureRequest.CONTROL_AE_MODE,
+ CaptureRequest.FLASH_MODE))) {
+ operations.add(RestrictedCameraControl.TORCH);
+ }
+
+ if (supportedParameterKeys.contains(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION)) {
+ operations.add(RestrictedCameraControl.EXPOSURE_COMPENSATION);
+ }
+ return operations;
+ }
+
+ @NonNull
private static SessionProcessorSurface createOutputConfigSurface(
@NonNull Camera2OutputConfig outputConfig, Map<Integer, ImageReader> imageReaderMap) {
if (outputConfig instanceof SurfaceOutputConfig) {
@@ -97,6 +164,7 @@
}
throw new UnsupportedOperationException("Unsupported Camera2OutputConfig:" + outputConfig);
}
+
@NonNull
@Override
@OptIn(markerClass = ExperimentalCamera2Interop.class)
@@ -163,6 +231,12 @@
}
@NonNull
+ @Override
+ public @CameraOperation Set<Integer> getSupportedCameraOperations() {
+ return mSupportedCameraOperations;
+ }
+
+ @NonNull
protected abstract Camera2SessionConfig initSessionInternal(
@NonNull String cameraId,
@NonNull Map<String, CameraCharacteristics> cameraCharacteristicsMap,
diff --git a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/SupportedCameraOperationsTest.kt b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/SupportedCameraOperationsTest.kt
new file mode 100644
index 0000000..0efc40a
--- /dev/null
+++ b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/SupportedCameraOperationsTest.kt
@@ -0,0 +1,457 @@
+/*
+ * 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.extensions.internal
+
+import android.content.Context
+import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CameraManager
+import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.CaptureResult
+import android.hardware.camera2.params.SessionConfiguration
+import android.os.Build
+import android.util.Pair
+import android.util.Range
+import android.util.Size
+import androidx.camera.camera2.internal.Camera2CameraInfoImpl
+import androidx.camera.camera2.internal.compat.CameraManagerCompat
+import androidx.camera.core.impl.RestrictedCameraControl
+import androidx.camera.extensions.impl.CaptureStageImpl
+import androidx.camera.extensions.impl.ImageCaptureExtenderImpl
+import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl
+import androidx.camera.extensions.impl.advanced.Camera2SessionConfigImpl
+import androidx.camera.extensions.impl.advanced.OutputSurfaceConfigurationImpl
+import androidx.camera.extensions.impl.advanced.OutputSurfaceImpl
+import androidx.camera.extensions.impl.advanced.RequestProcessorImpl
+import androidx.camera.extensions.impl.advanced.SessionProcessorImpl
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.ParameterizedRobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.shadow.api.Shadow
+import org.robolectric.shadows.ShadowCameraCharacteristics
+import org.robolectric.shadows.ShadowCameraManager
+
+@RunWith(ParameterizedRobolectricTestRunner::class)
+@DoNotInstrument
+@Config(
+ minSdk = Build.VERSION_CODES.LOLLIPOP,
+ instrumentedPackages = arrayOf("androidx.camera.extensions.internal")
+)
+class SupportedCameraOperationsTest(
+ private val extenderType: String
+) {
+ val context = RuntimeEnvironment.getApplication()
+
+ companion object {
+ @JvmStatic
+ @ParameterizedRobolectricTestRunner.Parameters(name = "{0}")
+ fun createTestSet(): List<String> {
+ return listOf("basic", "advanced")
+ }
+ }
+ private fun setCameraXExtensionsVersion(version: String) {
+ val field = VersionName::class.java.getDeclaredField("CURRENT")
+ field.isAccessible = true
+ field[null] = VersionName(version)
+ }
+
+ private fun setExtensionRuntimeVersion(version: String) {
+ ExtensionVersion.injectInstance(object : ExtensionVersion() {
+ override fun isAdvancedExtenderSupportedInternal(): Boolean {
+ return false
+ }
+
+ override fun getVersionObject(): Version {
+ return Version.parse(version)!!
+ }
+ })
+ }
+
+ @Before
+ fun setUp() {
+ setupCameraCharacteristics()
+ setCameraXExtensionsVersion("1.3.0")
+ setExtensionRuntimeVersion("1.3.0")
+ }
+
+ private fun setupCameraCharacteristics() {
+ val characteristics = ShadowCameraCharacteristics.newCameraCharacteristics()
+ val shadowCharacteristics = Shadow.extract<ShadowCameraCharacteristics>(characteristics)
+ shadowCharacteristics.set(
+ CameraCharacteristics.LENS_FACING, CameraCharacteristics.LENS_FACING_BACK
+ )
+ shadowCharacteristics.set(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES, arrayOf(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
+ )
+ )
+ val cameraManager = ApplicationProvider.getApplicationContext<Context>()
+ .getSystemService(Context.CAMERA_SERVICE) as CameraManager
+ (Shadow.extract<Any>(cameraManager) as ShadowCameraManager)
+ .addCamera("0", characteristics)
+ }
+
+ private fun testSupportedCameraOperation(
+ supportedCaptureRequestKeys: List<CaptureRequest.Key<out Any>>,
+ @RestrictedCameraControl.CameraOperation expectSupportedOperations: Set<Int>
+ ) {
+ var vendorExtender: VendorExtender? = null
+ if (extenderType == "basic") {
+ val fakeImageCaptureExtenderImpl = FakeImageCaptureExtenderImpl(
+ supportedRequestKeys = supportedCaptureRequestKeys
+ )
+ vendorExtender = BasicVendorExtender(fakeImageCaptureExtenderImpl, null)
+ } else if (extenderType == "advanced") {
+ val fakeAdvancedExtenderImpl = FakeAdvancedVendorExtenderImpl(
+ supportedRequestKeys = supportedCaptureRequestKeys
+ )
+ vendorExtender = AdvancedVendorExtender(fakeAdvancedExtenderImpl)
+ }
+
+ val cameraInfo = Camera2CameraInfoImpl("0", CameraManagerCompat.from(context))
+ vendorExtender!!.init(cameraInfo)
+ val sessionProcessor = vendorExtender.createSessionProcessor(context)!!
+ assertThat(sessionProcessor.supportedCameraOperations)
+ .containsExactlyElementsIn(expectSupportedOperations)
+ }
+
+ @Config(minSdk = Build.VERSION_CODES.R)
+ @Test
+ fun supportedCameraOperations_zoomIsEnabled_androidR() {
+ testSupportedCameraOperation(
+ supportedCaptureRequestKeys = listOf(
+ CaptureRequest.CONTROL_ZOOM_RATIO
+ ),
+ expectSupportedOperations = setOf(
+ RestrictedCameraControl.ZOOM
+ )
+ )
+ }
+
+ @Config(minSdk = Build.VERSION_CODES.R)
+ @Test
+ fun supportedCameraOperations_cropregion_zoomIsEnabled_androidR() {
+ testSupportedCameraOperation(
+ supportedCaptureRequestKeys = listOf(
+ CaptureRequest.SCALER_CROP_REGION
+ ),
+ expectSupportedOperations = setOf(
+ RestrictedCameraControl.ZOOM
+ )
+ )
+ }
+
+ @Config(maxSdk = Build.VERSION_CODES.Q)
+ @Test
+ fun supportedCameraOperations_zoomIsEnabled() {
+ testSupportedCameraOperation(
+ supportedCaptureRequestKeys = listOf(
+ CaptureRequest.SCALER_CROP_REGION
+ ),
+ expectSupportedOperations = setOf(
+ RestrictedCameraControl.ZOOM
+ )
+ )
+ }
+
+ @Test
+ fun supportedCameraOperations_autoFocusIsEnabled() {
+ testSupportedCameraOperation(
+ supportedCaptureRequestKeys = listOf(
+ CaptureRequest.CONTROL_AF_MODE,
+ CaptureRequest.CONTROL_AF_TRIGGER
+ ),
+ expectSupportedOperations = setOf(
+ RestrictedCameraControl.AUTO_FOCUS
+ )
+ )
+ }
+
+ @Test
+ fun supportedCameraOperations_afRegionIsEnabled() {
+ testSupportedCameraOperation(
+ supportedCaptureRequestKeys = listOf(
+ CaptureRequest.CONTROL_AF_REGIONS,
+ ),
+ expectSupportedOperations = setOf(
+ RestrictedCameraControl.AF_REGION
+ )
+ )
+ }
+
+ @Test
+ fun supportedCameraOperations_aeRegionIsEnabled() {
+ testSupportedCameraOperation(
+ supportedCaptureRequestKeys = listOf(
+ CaptureRequest.CONTROL_AE_REGIONS,
+ ),
+ expectSupportedOperations = setOf(
+ RestrictedCameraControl.AE_REGION
+ )
+ )
+ }
+
+ @Test
+ fun supportedCameraOperations_awbRegionIsEnabled() {
+ testSupportedCameraOperation(
+ supportedCaptureRequestKeys = listOf(
+ CaptureRequest.CONTROL_AWB_REGIONS,
+ ),
+ expectSupportedOperations = setOf(
+ RestrictedCameraControl.AWB_REGION
+ )
+ )
+ }
+
+ @Test
+ fun supportedCameraOperations_torchIsEnabled() {
+ testSupportedCameraOperation(
+ supportedCaptureRequestKeys = listOf(
+ CaptureRequest.CONTROL_AE_MODE,
+ CaptureRequest.FLASH_MODE
+ ),
+ expectSupportedOperations = setOf(
+ RestrictedCameraControl.TORCH
+ )
+ )
+ }
+
+ @Test
+ fun supportedCameraOperations_flashIsEnabled() {
+ testSupportedCameraOperation(
+ supportedCaptureRequestKeys = listOf(
+ CaptureRequest.CONTROL_AE_MODE,
+ CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER
+ ),
+ expectSupportedOperations = setOf(
+ RestrictedCameraControl.FLASH
+ )
+ )
+ }
+
+ @Test
+ fun supportedCameraOperations_exposureCompensationIsEnabled() {
+ testSupportedCameraOperation(
+ supportedCaptureRequestKeys = listOf(
+ CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION,
+ ),
+ expectSupportedOperations = setOf(
+ RestrictedCameraControl.EXPOSURE_COMPENSATION
+ )
+ )
+ }
+
+ // For Basic extender under 1.3.0, ensures all operations are supported
+ @Test
+ fun supportedCameraOperations_allOperationsEnabled_basic1_2_and_below() {
+ assumeTrue(extenderType == "basic")
+ setExtensionRuntimeVersion("1.2.0")
+ setCameraXExtensionsVersion("1.3.0")
+ testSupportedCameraOperation(
+ supportedCaptureRequestKeys = emptyList(),
+ expectSupportedOperations = setOf(
+ RestrictedCameraControl.ZOOM,
+ RestrictedCameraControl.AUTO_FOCUS,
+ RestrictedCameraControl.TORCH,
+ RestrictedCameraControl.AF_REGION,
+ RestrictedCameraControl.AE_REGION,
+ RestrictedCameraControl.AWB_REGION,
+ RestrictedCameraControl.EXPOSURE_COMPENSATION,
+ RestrictedCameraControl.FLASH,
+ )
+ )
+ }
+
+ @Test
+ fun supportedCameraOperations_allOperationsDisabled_advanced1_2_and_below() {
+ assumeTrue(extenderType == "advanced")
+ setExtensionRuntimeVersion("1.2.0")
+ setCameraXExtensionsVersion("1.3.0")
+ testSupportedCameraOperation(
+ supportedCaptureRequestKeys = listOf(
+ CaptureRequest.SCALER_CROP_REGION,
+ CaptureRequest.CONTROL_AF_MODE,
+ CaptureRequest.CONTROL_AF_TRIGGER,
+ CaptureRequest.CONTROL_AF_REGIONS,
+ CaptureRequest.CONTROL_AE_REGIONS,
+ CaptureRequest.CONTROL_AWB_REGIONS,
+ CaptureRequest.CONTROL_AE_MODE,
+ CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
+ CaptureRequest.FLASH_MODE,
+ CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION
+ ),
+ expectSupportedOperations = emptySet() // No ops should be supported.
+ )
+ }
+
+ private class FakeImageCaptureExtenderImpl(
+ val supportedRequestKeys: List<CaptureRequest.Key<out Any>>
+ ) : ImageCaptureExtenderImpl {
+ override fun isExtensionAvailable(
+ cameraId: String,
+ cameraCharacteristics: CameraCharacteristics
+ ): Boolean = true
+ override fun init(cameraId: String, cameraCharacteristics: CameraCharacteristics) {
+ }
+ override fun getCaptureProcessor() = null
+ override fun getCaptureStages(): List<CaptureStageImpl> = emptyList()
+ override fun getMaxCaptureStage() = 2
+ override fun getSupportedResolutions() = null
+ override fun getEstimatedCaptureLatencyRange(size: Size?) = null
+ override fun getAvailableCaptureRequestKeys(): List<CaptureRequest.Key<out Any>> {
+ return supportedRequestKeys
+ }
+
+ override fun getAvailableCaptureResultKeys(): List<CaptureResult.Key<Any>> {
+ return mutableListOf()
+ }
+
+ override fun getSupportedPostviewResolutions(
+ captureSize: Size
+ ): MutableList<Pair<Int, Array<Size>>>? = null
+
+ override fun isCaptureProcessProgressAvailable() = false
+
+ override fun getRealtimeCaptureLatency(): Pair<Long, Long>? = null
+ override fun isPostviewAvailable() = false
+ override fun onInit(
+ cameraId: String,
+ cameraCharacteristics: CameraCharacteristics,
+ context: Context
+ ) {}
+
+ override fun onDeInit() {}
+ override fun onPresetSession(): CaptureStageImpl? = null
+
+ override fun onEnableSession(): CaptureStageImpl? = null
+
+ override fun onDisableSession(): CaptureStageImpl? = null
+ override fun onSessionType(): Int = SessionConfiguration.SESSION_REGULAR
+ }
+
+ private class FakeAdvancedVendorExtenderImpl(
+ val supportedRequestKeys: List<CaptureRequest.Key<out Any>>
+ ) : AdvancedExtenderImpl {
+ override fun isExtensionAvailable(
+ cameraId: String,
+ characteristicsMap: MutableMap<String, CameraCharacteristics>
+ ): Boolean = true
+
+ override fun init(
+ cameraId: String,
+ characteristicsMap: MutableMap<String, CameraCharacteristics>
+ ) {}
+ override fun getEstimatedCaptureLatencyRange(
+ cameraId: String,
+ captureOutputSize: Size?,
+ imageFormat: Int
+ ): Range<Long>? = null
+ override fun getSupportedPreviewOutputResolutions(
+ cameraId: String
+ ): Map<Int, MutableList<Size>> = emptyMap()
+ override fun getSupportedCaptureOutputResolutions(
+ cameraId: String
+ ): Map<Int, MutableList<Size>> = emptyMap()
+
+ override fun getSupportedPostviewResolutions(
+ captureSize: Size
+ ): Map<Int, MutableList<Size>> = emptyMap()
+ override fun getSupportedYuvAnalysisResolutions(cameraId: String) = null
+ override fun createSessionProcessor(): SessionProcessorImpl = DummySessionProcessorImpl()
+ override fun getAvailableCaptureRequestKeys():
+ List<CaptureRequest.Key<out Any>> = supportedRequestKeys
+
+ override fun getAvailableCaptureResultKeys(): List<CaptureResult.Key<Any>> = emptyList()
+ override fun isCaptureProcessProgressAvailable() = false
+ override fun isPostviewAvailable() = false
+ }
+
+ private class DummySessionProcessorImpl : SessionProcessorImpl {
+ override fun initSession(
+ cameraId: String,
+ cameraCharacteristicsMap: MutableMap<String, CameraCharacteristics>,
+ context: Context,
+ surfaceConfigs: OutputSurfaceConfigurationImpl
+ ): Camera2SessionConfigImpl {
+ throw UnsupportedOperationException("Not supported")
+ }
+ override fun initSession(
+ cameraId: String,
+ cameraCharacteristicsMap: MutableMap<String, CameraCharacteristics>,
+ context: Context,
+ previewSurfaceConfig: OutputSurfaceImpl,
+ imageCaptureSurfaceConfig: OutputSurfaceImpl,
+ imageAnalysisSurfaceConfig: OutputSurfaceImpl?
+ ): Camera2SessionConfigImpl {
+ throw UnsupportedOperationException("Not supported")
+ }
+
+ override fun deInitSession() {
+ throw UnsupportedOperationException("Not supported")
+ }
+
+ override fun setParameters(parameters: MutableMap<CaptureRequest.Key<*>, Any>) {
+ throw UnsupportedOperationException("Not supported")
+ }
+
+ override fun startTrigger(
+ triggers: MutableMap<CaptureRequest.Key<*>, Any>,
+ callback: SessionProcessorImpl.CaptureCallback
+ ): Int {
+ throw UnsupportedOperationException("Not supported")
+ }
+
+ override fun onCaptureSessionStart(requestProcessor: RequestProcessorImpl) {
+ throw UnsupportedOperationException("Not supported")
+ }
+
+ override fun onCaptureSessionEnd() {
+ throw UnsupportedOperationException("Not supported")
+ }
+
+ override fun startRepeating(callback: SessionProcessorImpl.CaptureCallback): Int {
+ throw UnsupportedOperationException("Not supported")
+ }
+
+ override fun stopRepeating() {
+ throw UnsupportedOperationException("Not supported")
+ }
+
+ override fun startCapture(callback: SessionProcessorImpl.CaptureCallback): Int {
+ throw UnsupportedOperationException("Not supported")
+ }
+
+ override fun startCaptureWithPostview(callback: SessionProcessorImpl.CaptureCallback): Int {
+ throw UnsupportedOperationException("Not supported")
+ }
+
+ override fun abortCapture(captureSequenceId: Int) {
+ throw UnsupportedOperationException("Not supported")
+ }
+
+ override fun getRealtimeCaptureLatency(): Pair<Long, Long>? {
+ throw UnsupportedOperationException("Not supported")
+ }
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/workaround/AvailableKeysRetrieverTest.kt b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/workaround/AvailableKeysRetrieverTest.kt
new file mode 100644
index 0000000..816aaf4
--- /dev/null
+++ b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/workaround/AvailableKeysRetrieverTest.kt
@@ -0,0 +1,137 @@
+/*
+ * 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.extensions.internal.compat.workaround
+
+import android.content.Context
+import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.CaptureResult
+import android.hardware.camera2.params.SessionConfiguration
+import android.os.Build
+import android.util.Pair
+import android.util.Size
+import androidx.camera.extensions.impl.CaptureStageImpl
+import androidx.camera.extensions.impl.ImageCaptureExtenderImpl
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.shadows.ShadowCameraCharacteristics
+import org.robolectric.util.ReflectionHelpers
+
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(
+ minSdk = Build.VERSION_CODES.LOLLIPOP,
+ instrumentedPackages = arrayOf("androidx.camera.extensions.internal")
+)
+class AvailableKeysRetrieverTest {
+ private val context: Context = RuntimeEnvironment.getApplication()
+ private val availableKeys = listOf<CaptureRequest.Key<out Any>>(
+ CaptureRequest.CONTROL_AE_REGIONS, CaptureRequest.CONTROL_AF_MODE
+ )
+ private val fakeImageCaptureExtenderImpl = FakeImageCaptureExtenderImpl(availableKeys)
+ private val characteristics = ShadowCameraCharacteristics.newCameraCharacteristics()
+
+ @Test
+ fun shouldInvokeOnInit() {
+ // 1. Arrange
+ ReflectionHelpers.setStaticField(Build::class.java, "BRAND", "SAMSUNG")
+ val retriever = AvailableKeysRetriever()
+
+ // 2. Act
+ val resultKeys = retriever.getAvailableCaptureRequestKeys(
+ fakeImageCaptureExtenderImpl, "0", characteristics, context)
+
+ // 3. Assert
+ assertThat(resultKeys).containsExactlyElementsIn(availableKeys)
+ assertThat(fakeImageCaptureExtenderImpl.invokeList).containsExactly(
+ "onInit", "getAvailableCaptureRequestKeys", "onDeInit"
+ )
+ }
+
+ @Test
+ fun shouldNotInvokeOnInit() {
+ // 1. Arrange
+ ReflectionHelpers.setStaticField(Build::class.java, "BRAND", "OTHER")
+ val retriever = AvailableKeysRetriever()
+
+ // 2. Act
+ val resultKeys = retriever.getAvailableCaptureRequestKeys(
+ fakeImageCaptureExtenderImpl, "0", characteristics, context)
+
+ // 3. Assert
+ assertThat(resultKeys).containsExactlyElementsIn(availableKeys)
+ assertThat(fakeImageCaptureExtenderImpl.invokeList).containsExactly(
+ "getAvailableCaptureRequestKeys"
+ )
+ }
+
+ class FakeImageCaptureExtenderImpl(
+ private var availableRequestKeys: List<CaptureRequest.Key<out Any>>
+ ) : ImageCaptureExtenderImpl {
+ val invokeList = mutableListOf<String>()
+ override fun isExtensionAvailable(
+ cameraId: String,
+ cameraCharacteristics: CameraCharacteristics
+ ): Boolean = true
+ override fun init(cameraId: String, cameraCharacteristics: CameraCharacteristics) {
+ invokeList.add("init")
+ }
+ override fun getCaptureProcessor() = null
+ override fun getCaptureStages(): List<CaptureStageImpl> = emptyList()
+ override fun getMaxCaptureStage() = 2
+ override fun getSupportedResolutions() = null
+ override fun getEstimatedCaptureLatencyRange(size: Size?) = null
+ override fun getAvailableCaptureRequestKeys(): List<CaptureRequest.Key<out Any>> {
+ invokeList.add("getAvailableCaptureRequestKeys")
+
+ return availableRequestKeys
+ }
+
+ override fun getAvailableCaptureResultKeys(): List<CaptureResult.Key<Any>> {
+ return mutableListOf()
+ }
+
+ override fun getSupportedPostviewResolutions(
+ captureSize: Size
+ ): MutableList<Pair<Int, Array<Size>>>? = null
+
+ override fun isCaptureProcessProgressAvailable() = false
+
+ override fun getRealtimeCaptureLatency(): Pair<Long, Long>? = null
+ override fun isPostviewAvailable() = false
+ override fun onInit(
+ cameraId: String,
+ cameraCharacteristics: CameraCharacteristics,
+ context: Context
+ ) {
+ invokeList.add("onInit")
+ }
+
+ override fun onDeInit() {
+ invokeList.add("onDeInit")
+ }
+ override fun onPresetSession(): CaptureStageImpl? = null
+ override fun onEnableSession(): CaptureStageImpl? = null
+ override fun onDisableSession(): CaptureStageImpl? = null
+ override fun onSessionType(): Int = SessionConfiguration.SESSION_REGULAR
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-testing/build.gradle b/camera/camera-testing/build.gradle
index b0774f2..177e3db 100644
--- a/camera/camera-testing/build.gradle
+++ b/camera/camera-testing/build.gradle
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-
-import androidx.build.LibraryType
import androidx.build.Publish
+import androidx.build.RunApiTasks
plugins {
id("AndroidXPlugin")
@@ -93,8 +92,8 @@
androidx {
name = "Jetpack Camera Testing Library"
- type = LibraryType.INTERNAL_TEST_LIBRARY
- publish = Publish.SNAPSHOT_ONLY
+ publish = Publish.SNAPSHOT_AND_RELEASE
+ runApiTasks = new RunApiTasks.No("Internal testing library without any release plan yet.")
inceptionYear = "2019"
description = "Testing components for the Jetpack Camera Library, a library providing a " +
"consistent and reliable camera foundation that enables great camera driven " +"" +
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/TestImageUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/TestImageUtil.java
index b04224a..1054b0a 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/TestImageUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/TestImageUtil.java
@@ -26,6 +26,7 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
@@ -129,6 +130,17 @@
}
/**
+ * Rotates the bitmap clockwise by the given degrees.
+ */
+ @NonNull
+ public static Bitmap rotateBitmap(@NonNull Bitmap bitmap, int rotationDegrees) {
+ Matrix matrix = new Matrix();
+ matrix.postRotate(rotationDegrees);
+ return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix,
+ true);
+ }
+
+ /**
* Calculates the average color difference between the 2 JPEG images.
*/
public static int getAverageDiff(@NonNull byte[] jpeg1, @NonNull byte[] jpeg2) {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
index c032e08..73c441f 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
@@ -23,6 +23,7 @@
import android.util.Size;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.FocusMeteringResult;
@@ -80,6 +81,10 @@
private float mZoomRatio = -1;
private float mLinearZoom = -1;
private boolean mTorchEnabled = false;
+ private int mExposureCompensation = -1;
+
+ @Nullable
+ private FocusMeteringAction mLastSubmittedFocusMeteringAction = null;
public FakeCameraControl() {
this(NO_OP_CALLBACK);
@@ -189,9 +194,14 @@
@NonNull
@Override
public ListenableFuture<Integer> setExposureCompensationIndex(int exposure) {
+ mExposureCompensation = exposure;
return Futures.immediateFuture(null);
}
+ public int getExposureCompensationIndex() {
+ return mExposureCompensation;
+ }
+
@NonNull
@Override
public ListenableFuture<List<Void>> submitStillCaptureRequests(
@@ -229,6 +239,7 @@
@Override
public ListenableFuture<FocusMeteringResult> startFocusAndMetering(
@NonNull FocusMeteringAction action) {
+ mLastSubmittedFocusMeteringAction = action;
return Futures.immediateFuture(FocusMeteringResult.emptyInstance());
}
@@ -265,6 +276,11 @@
return mLinearZoom;
}
+ @Nullable
+ public FocusMeteringAction getLastSubmittedFocusMeteringAction() {
+ return mLastSubmittedFocusMeteringAction;
+ }
+
@Override
public void addInteropConfig(@NonNull Config config) {
for (Config.Option<?> option : config.listOptions()) {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
index 6fb7552..7a4b53a 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
@@ -92,6 +92,9 @@
private boolean mIsPrivateReprocessingSupported = false;
private float mIntrinsicZoomRatio = 1.0F;
+ private boolean mIsFocusMeteringSupported = false;
+
+ private ExposureState mExposureState = new FakeExposureState();
@NonNull
private final List<Quirk> mCameraQuirks = new ArrayList<>();
@@ -117,6 +120,37 @@
mZoomLiveData = new MutableLiveData<>(ImmutableZoomState.create(1.0f, 4.0f, 1.0f, 0.0f));
}
+ /**
+ * Sets the zoom parameter.
+ */
+ public void setZoom(float zoomRatio, float minZoomRatio, float maxZoomRatio, float linearZoom) {
+ mZoomLiveData.postValue(ImmutableZoomState.create(
+ zoomRatio, maxZoomRatio, minZoomRatio, linearZoom
+ ));
+ }
+
+ /**
+ * Sets the exposure compensation parameters.
+ */
+ public void setExposureState(int index, @NonNull Range<Integer> range,
+ @NonNull Rational step, boolean isSupported) {
+ mExposureState = new FakeExposureState(index, range, step, isSupported);
+ }
+
+ /**
+ * Sets the torch state.
+ */
+ public void setTorch(int torchState) {
+ mTorchState.postValue(torchState);
+ }
+
+ /**
+ * Sets the return value for {@link #isFocusMeteringSupported(FocusMeteringAction)}.
+ */
+ public void setIsFocusMeteringSupported(boolean supported) {
+ mIsFocusMeteringSupported = supported;
+ }
+
@Override
public int getLensFacing() {
return mLensFacing;
@@ -169,7 +203,7 @@
@NonNull
@Override
public ExposureState getExposureState() {
- return new FakeExposureState();
+ return mExposureState;
}
@NonNull
@@ -246,7 +280,7 @@
@Override
public boolean isFocusMeteringSupported(@NonNull FocusMeteringAction action) {
- return false;
+ return mIsFocusMeteringSupported;
}
@Override
@@ -317,26 +351,41 @@
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
static final class FakeExposureState implements ExposureState {
+ private int mIndex = 0;
+ private Range<Integer> mRange = new Range<>(0, 0);
+ private Rational mStep = Rational.ZERO;
+ private boolean mIsSupported = true;
+
+ FakeExposureState() {
+ }
+ FakeExposureState(int index, Range<Integer> range,
+ Rational step, boolean isSupported) {
+ mIndex = index;
+ mRange = range;
+ mStep = step;
+ mIsSupported = isSupported;
+ }
+
@Override
public int getExposureCompensationIndex() {
- return 0;
+ return mIndex;
}
@NonNull
@Override
public Range<Integer> getExposureCompensationRange() {
- return Range.create(0, 0);
+ return mRange;
}
@NonNull
@Override
public Rational getExposureCompensationStep() {
- return Rational.ZERO;
+ return mStep;
}
@Override
public boolean isExposureCompensationSupported() {
- return true;
+ return mIsSupported;
}
}
}
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSessionProcessor.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSessionProcessor.kt
index e08f64e..ee51c7d 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSessionProcessor.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSessionProcessor.kt
@@ -33,6 +33,7 @@
import androidx.camera.core.impl.OptionsBundle
import androidx.camera.core.impl.OutputSurface
import androidx.camera.core.impl.RequestProcessor
+import androidx.camera.core.impl.RestrictedCameraControl
import androidx.camera.core.impl.SessionConfig
import androidx.camera.core.impl.SessionProcessor
import androidx.camera.core.impl.SessionProcessorSurface
@@ -46,8 +47,8 @@
@RequiresApi(28) // writing to PRIVATE surface requires API 28+
class FakeSessionProcessor(
- val inputFormatPreview: Int?,
- val inputFormatCapture: Int?
+ val inputFormatPreview: Int? = null,
+ val inputFormatCapture: Int? = null
) : SessionProcessor {
private lateinit var previewProcessorSurface: DeferrableSurface
private lateinit var captureProcessorSurface: DeferrableSurface
@@ -77,6 +78,9 @@
private var rotationDegrees = 0
private var jpegQuality = 100
+ @RestrictedCameraControl.CameraOperation
+ var restrictedCameraOperations: Set<Int> = emptySet()
+
fun releaseSurfaces() {
intermediaPreviewImageReader?.close()
intermediaCaptureImageReader?.close()
@@ -215,6 +219,11 @@
return latestParameters
}
+ @RestrictedCameraControl.CameraOperation
+ override fun getSupportedCameraOperations(): Set<Int> {
+ return restrictedCameraOperations
+ }
+
override fun startRepeating(callback: SessionProcessor.CaptureCallback): Int {
startRepeatingCalled.complete(SystemClock.elapsedRealtimeNanos())
val builder = RequestProcessorRequest.Builder().apply {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSurfaceProcessorInternal.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSurfaceProcessorInternal.java
index 94bd4cc..243d6ee 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSurfaceProcessorInternal.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSurfaceProcessorInternal.java
@@ -37,6 +37,7 @@
private boolean mIsReleased;
private int mJpegQuality = 0;
+ private int mRotationDegrees = 0;
/**
* {@inheritDoc}
@@ -65,8 +66,11 @@
@Override
@NonNull
- public ListenableFuture<Void> snapshot(int jpegQuality) {
+ public ListenableFuture<Void> snapshot(
+ @IntRange(from = 0, to = 100) int jpegQuality,
+ @IntRange(from = 0, to = 359) int rotationDegrees) {
mJpegQuality = jpegQuality;
+ mRotationDegrees = rotationDegrees;
return Futures.immediateFuture(null);
}
@@ -74,4 +78,9 @@
public int getJpegQuality() {
return mJpegQuality;
}
+
+ @IntRange(from = 0, to = 359)
+ public int getRotationDegrees() {
+ return mRotationDegrees;
+ }
}
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCase.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCase.java
index 6b5abe0..2e1ec2f 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCase.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCase.java
@@ -85,6 +85,7 @@
@Override
public UseCaseConfig.Builder<?, ?, ?> getUseCaseConfigBuilder(@NonNull Config config) {
return new FakeUseCaseConfig.Builder(config)
+ .setCaptureType(mCaptureType)
.setSessionOptionUnpacker((resolution, useCaseConfig, sessionConfigBuilder) -> {
});
}
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
index 88ae896..12a104a 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
@@ -274,5 +274,12 @@
getMutableConfig().insertOption(OPTION_HIGH_RESOLUTION_DISABLED, disabled);
return this;
}
+
+ @NonNull
+ @Override
+ public Builder setCaptureType(@NonNull CaptureType captureType) {
+ getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
+ return this;
+ }
}
}
diff --git a/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java b/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
index aafc3572..b5ba2a4 100644
--- a/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
+++ b/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
@@ -38,6 +38,7 @@
import androidx.camera.core.impl.StreamSpec;
import androidx.camera.core.impl.SurfaceConfig;
import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.impl.UseCaseConfigFactory;
import org.junit.Before;
import org.junit.Test;
@@ -105,6 +106,8 @@
YUV_420_888,
new Size(1, 1),
DynamicRange.SDR,
+ singletonList(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS),
+ preview,
new Range<>(30, 30));
mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
CameraMode.DEFAULT,
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt
index 6bbe1fe..8366234 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt
@@ -847,29 +847,6 @@
}
@Test
- fun mute_defaultToNotMuted() {
- // Arrange.
- val recorder = createRecorder()
- val recording = createRecordingProcess(recorder = recorder)
- val recording2 = createRecordingProcess(recorder = recorder)
-
- // Act.
- recording.startAndVerify()
- recording.mute(true)
- recording.stopAndVerify()
-
- recording2.startAndVerify()
- recording2.verifyStatus(5) { statusList ->
- // Assert.
- statusList.forEach {
- assertThat(it.recordingStats.audioStats.audioState)
- .isEqualTo(AudioStats.AUDIO_STATE_ACTIVE)
- }
- }
- recording2.stopAndVerify()
- }
-
- @Test
fun optionsOverridesDefaults() {
val qualitySelector = QualitySelector.from(Quality.HIGHEST)
val recorder = createRecorder(qualitySelector = qualitySelector)
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt
index 892b739..5a9196e 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt
@@ -27,7 +27,11 @@
import androidx.camera.core.CameraSelector
import androidx.camera.core.CameraXConfig
import androidx.camera.core.DynamicRange
+import androidx.camera.core.DynamicRange.BIT_DEPTH_10_BIT
+import androidx.camera.core.DynamicRange.FORMAT_HLG
+import androidx.camera.core.DynamicRange.HDR_UNSPECIFIED_10_BIT
import androidx.camera.core.SurfaceRequest
+import androidx.camera.core.impl.CameraInfoInternal
import androidx.camera.core.impl.MutableStateObservable
import androidx.camera.core.impl.Observable
import androidx.camera.core.internal.CameraUseCaseAdapter
@@ -127,7 +131,7 @@
)
private lateinit var cameraUseCaseAdapter: CameraUseCaseAdapter
- private lateinit var cameraInfo: CameraInfo
+ private lateinit var cameraInfo: CameraInfoInternal
@Before
fun setUp() {
@@ -137,7 +141,7 @@
).get()
cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(context, cameraSelector)
- cameraInfo = cameraUseCaseAdapter.cameraInfo
+ cameraInfo = cameraUseCaseAdapter.cameraInfo as CameraInfoInternal
}
@After
@@ -376,6 +380,70 @@
} ?: fail("Timed out waiting for INACTIVE state. Waited $timeout.")
}
+ @Test
+ fun defaultDynamicRange_isSdr(): Unit = runBlocking {
+ testDynamicRangeSelection { selectedDynamicRange ->
+ assertThat(selectedDynamicRange).isEqualTo(DynamicRange.SDR)
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = 33) // HLG10 only supported on API 33+
+ @Test
+ fun dynamicRangeHlg_selectsHlg(): Unit = runBlocking {
+ val hlg10DynamicRange = DynamicRange(FORMAT_HLG, BIT_DEPTH_10_BIT)
+ assumeTrue(
+ "Device does not support HLG10",
+ cameraInfo.supportedDynamicRanges.contains(hlg10DynamicRange)
+ )
+
+ testDynamicRangeSelection(
+ requestedDynamicRange = hlg10DynamicRange
+ ) { selectedDynamicRange ->
+ assertThat(selectedDynamicRange).isEqualTo(hlg10DynamicRange)
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = 33) // 10-bit HDR only supported on API 33+
+ @Test
+ fun dynamicRangeHdrUnspecified10Bit_selectsHdr10Bit(): Unit = runBlocking {
+ val supported10BitDynamicRanges = cameraInfo.supportedDynamicRanges.filter {
+ it.bitDepth == BIT_DEPTH_10_BIT
+ }
+ assumeFalse(
+ "Device does not support any 10-bit dynamic ranges",
+ supported10BitDynamicRanges.isEmpty()
+ )
+
+ testDynamicRangeSelection(
+ requestedDynamicRange = HDR_UNSPECIFIED_10_BIT
+ ) { selectedDynamicRange ->
+ assertThat(selectedDynamicRange).isIn(supported10BitDynamicRanges)
+ }
+ }
+
+ private suspend fun testDynamicRangeSelection(
+ requestedDynamicRange: DynamicRange? = null,
+ assertBlock: (selectedDynamicRange: DynamicRange) -> Unit
+ ) {
+ // TODO(b/275632219): Disabled on camera-pipe until automatic dynamic range
+ // selection is supported
+ assumeTrue(implName != CameraPipeConfig::class.simpleName)
+ // Arrange.
+ val videoOutput = createTestVideoOutput()
+ val videoCapture = VideoCapture.Builder(videoOutput).apply {
+ requestedDynamicRange?.let { setDynamicRange(requestedDynamicRange) }
+ }.build()
+
+ // Act.
+ withContext(Dispatchers.Main) {
+ cameraUseCaseAdapter.addUseCases(listOf(videoCapture))
+ }
+
+ // Assert.
+ val surfaceRequest = videoOutput.nextSurfaceRequest(5, TimeUnit.SECONDS)
+ assertBlock(surfaceRequest.dynamicRange)
+ }
+
private fun createTestVideoOutput(
streamInfo: StreamInfo = StreamInfo.of(
StreamInfo.STREAM_ID_ANY,
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 2a79dbd..7e62cf4 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
@@ -846,6 +846,73 @@
file2.delete()
}
+ @Test
+ fun mute_defaultToNotMuted() {
+ assumeTrue("Audio stream is not available", audioStreamAvailable)
+
+ // Arrange.
+ val recorder = Recorder.Builder().build()
+ val videoCaptureLocal = VideoCapture.withOutput(recorder)
+ instrumentation.runOnMainSync {
+ cameraProvider.bindToLifecycle(
+ lifecycleOwner,
+ cameraSelector,
+ preview,
+ videoCaptureLocal
+ )
+ }
+ val file1 = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
+
+ recorder.prepareRecording(context, FileOutputOptions.Builder(file1).build())
+ .withAudioEnabled()
+ .start(CameraXExecutors.directExecutor(), mockVideoRecordEventConsumer).use {
+ mockVideoRecordEventConsumer.verifyRecordingStartSuccessfully()
+ // Keep the first recording muted.
+ it.mute(true)
+ }
+
+ mockVideoRecordEventConsumer.verifyAcceptCall(
+ VideoRecordEvent.Finalize::class.java,
+ false,
+ GENERAL_TIMEOUT
+ )
+ file1.delete()
+
+ mockVideoRecordEventConsumer.clearAcceptCalls()
+
+ val file2 = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
+
+ // Act.
+ recorder.prepareRecording(context, FileOutputOptions.Builder(file2).build())
+ .withAudioEnabled()
+ .start(CameraXExecutors.directExecutor(), mockVideoRecordEventConsumer).use {
+ mockVideoRecordEventConsumer.verifyRecordingStartSuccessfully()
+ val captor = ArgumentCaptorCameraX<VideoRecordEvent> { argument ->
+ VideoRecordEvent::class.java.isInstance(
+ argument
+ )
+ }
+ mockVideoRecordEventConsumer.verifyAcceptCall(
+ VideoRecordEvent::class.java,
+ false,
+ CallTimesAtLeast(1),
+ captor
+ )
+ assertThat(captor.value).isInstanceOf(VideoRecordEvent.Status::class.java)
+ val status = captor.value as VideoRecordEvent.Status
+ // Assert: The second recording should not be muted.
+ assertThat(status.recordingStats.audioStats.audioState)
+ .isEqualTo(AudioStats.AUDIO_STATE_ACTIVE)
+ }
+
+ mockVideoRecordEventConsumer.verifyAcceptCall(
+ VideoRecordEvent.Finalize::class.java,
+ false,
+ GENERAL_TIMEOUT
+ )
+ file2.delete()
+ }
+
private fun performRecording(
videoCapture: VideoCapture<Recorder>,
file: File,
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/SharedByteBufferTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/SharedByteBufferTest.kt
index af3ed21..58e7d60 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/SharedByteBufferTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/SharedByteBufferTest.kt
@@ -16,6 +16,7 @@
package androidx.camera.video.internal
+import android.os.Build
import androidx.camera.core.impl.utils.executor.CameraXExecutors
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
@@ -35,6 +36,7 @@
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
+import org.junit.Assume.assumeFalse
import org.junit.Test
import org.junit.runner.RunWith
@@ -289,6 +291,11 @@
@Test
@LargeTest
fun finalizeClosesUnclosedInstances() = runBlocking {
+ assumeFalse(
+ "Ignore devices that get flaky result. See b/278842333",
+ isModel("moto c") || isModel("rne-l23")
+ )
+
val buf = ByteBuffer.allocate(0)
val closeActionDeferred = CompletableDeferred<Unit>()
val origBuf = SharedByteBuffer.newSharedInstance(buf, CameraXExecutors.directExecutor()) {
@@ -327,4 +334,6 @@
phantomReferences.forEach { it.clear() }
}
}
+
+ private fun isModel(model: String) = model.equals(Build.MODEL, true)
}
\ No newline at end of file
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/RecorderVideoCapabilities.java b/camera/camera-video/src/main/java/androidx/camera/video/RecorderVideoCapabilities.java
index 0c30441..ef2fd4c 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/RecorderVideoCapabilities.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/RecorderVideoCapabilities.java
@@ -16,11 +16,12 @@
package androidx.camera.video;
+import static androidx.camera.core.DynamicRange.BIT_DEPTH_UNSPECIFIED;
+import static androidx.camera.core.DynamicRange.FORMAT_HDR_UNSPECIFIED;
import static androidx.camera.core.DynamicRange.FORMAT_HLG;
-import static androidx.camera.core.DynamicRange.HDR_UNSPECIFIED_10_BIT;
-import static androidx.camera.core.DynamicRange.SDR;
+import static androidx.camera.core.DynamicRange.FORMAT_SDR;
+import static androidx.camera.core.DynamicRange.FORMAT_UNSPECIFIED;
import static androidx.camera.video.internal.BackupHdrProfileEncoderProfilesProvider.DEFAULT_VALIDATOR;
-import static androidx.camera.video.internal.utils.DynamicRangeUtil.VP_TO_DR_FORMAT_MAP;
import android.util.Size;
@@ -51,7 +52,6 @@
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -74,7 +74,17 @@
private static final String TAG = "RecorderVideoCapabilities";
- private final Map<DynamicRange, CapabilitiesByQuality> mCapabilitiesMap = new HashMap<>();
+ private final EncoderProfilesProvider mProfilesProvider;
+
+ // Mappings of DynamicRange to recording capability information. The mappings are divided
+ // into two collections based on the key's (DynamicRange) category, one for specified
+ // DynamicRange and one for others. Specified DynamicRange means that its bit depth and
+ // format are specified values, not some wildcards, such as: FORMAT_UNSPECIFIED,
+ // FORMAT_HDR_UNSPECIFIED or BIT_DEPTH_UNSPECIFIED.
+ private final Map<DynamicRange, CapabilitiesByQuality>
+ mCapabilitiesMapForFullySpecifiedDynamicRange = new HashMap<>();
+ private final Map<DynamicRange, CapabilitiesByQuality>
+ mCapabilitiesMapForNonFullySpecifiedDynamicRange = new HashMap<>();
/**
* Creates a RecorderVideoCapabilities.
@@ -106,23 +116,18 @@
Quirks deviceQuirks = DeviceQuirks.getAll();
encoderProfilesProvider = new QualityValidatedEncoderProfilesProvider(
encoderProfilesProvider, cameraInfoInternal, deviceQuirks);
+ mProfilesProvider = encoderProfilesProvider;
// Group by dynamic range.
- for (DynamicRange dynamicRange : getCandidateDynamicRanges(encoderProfilesProvider)) {
- if (!isDynamicRangeSupported(dynamicRange, cameraInfoInternal)) {
- continue;
- }
-
+ for (DynamicRange dynamicRange : cameraInfoInternal.getSupportedDynamicRanges()) {
// Filter video profiles to include only the profiles match with the target dynamic
// range.
EncoderProfilesProvider constrainedProvider =
- new DynamicRangeMatchedEncoderProfilesProvider(encoderProfilesProvider,
- dynamicRange);
- CapabilitiesByQuality capabilitiesByQuality =
- new CapabilitiesByQuality(constrainedProvider);
+ new DynamicRangeMatchedEncoderProfilesProvider(mProfilesProvider, dynamicRange);
+ CapabilitiesByQuality capabilities = new CapabilitiesByQuality(constrainedProvider);
- if (!capabilitiesByQuality.getSupportedQualities().isEmpty()) {
- mCapabilitiesMap.put(dynamicRange, capabilitiesByQuality);
+ if (!capabilities.getSupportedQualities().isEmpty()) {
+ mCapabilitiesMapForFullySpecifiedDynamicRange.put(dynamicRange, capabilities);
}
}
}
@@ -144,25 +149,20 @@
@NonNull
@Override
public Set<DynamicRange> getSupportedDynamicRanges() {
- Set<DynamicRange> dynamicRanges = mCapabilitiesMap.keySet();
-
- // Remove HDR_UNSPECIFIED_10_BIT from output, since it does not have explicit content.
- dynamicRanges.remove(HDR_UNSPECIFIED_10_BIT);
-
- return dynamicRanges;
+ return mCapabilitiesMapForFullySpecifiedDynamicRange.keySet();
}
@NonNull
@Override
public List<Quality> getSupportedQualities(@NonNull DynamicRange dynamicRange) {
- CapabilitiesByQuality capabilities = mCapabilitiesMap.get(dynamicRange);
+ CapabilitiesByQuality capabilities = getCapabilities(dynamicRange);
return capabilities == null ? new ArrayList<>() : capabilities.getSupportedQualities();
}
@Override
public boolean isQualitySupported(@NonNull Quality quality,
@NonNull DynamicRange dynamicRange) {
- CapabilitiesByQuality capabilities = mCapabilitiesMap.get(dynamicRange);
+ CapabilitiesByQuality capabilities = getCapabilities(dynamicRange);
return capabilities != null && capabilities.isQualitySupported(quality);
}
@@ -170,7 +170,7 @@
@Override
public VideoValidatedEncoderProfilesProxy getProfiles(@NonNull Quality quality,
@NonNull DynamicRange dynamicRange) {
- CapabilitiesByQuality capabilities = mCapabilitiesMap.get(dynamicRange);
+ CapabilitiesByQuality capabilities = getCapabilities(dynamicRange);
return capabilities == null ? null : capabilities.getProfiles(quality);
}
@@ -178,7 +178,7 @@
@Override
public VideoValidatedEncoderProfilesProxy findHighestSupportedEncoderProfilesFor(
@NonNull Size size, @NonNull DynamicRange dynamicRange) {
- CapabilitiesByQuality capabilities = mCapabilitiesMap.get(dynamicRange);
+ CapabilitiesByQuality capabilities = getCapabilities(dynamicRange);
return capabilities == null ? null : capabilities.findHighestSupportedEncoderProfilesFor(
size);
}
@@ -187,57 +187,28 @@
@Override
public Quality findHighestSupportedQualityFor(@NonNull Size size,
@NonNull DynamicRange dynamicRange) {
- CapabilitiesByQuality capabilities = mCapabilitiesMap.get(dynamicRange);
+ CapabilitiesByQuality capabilities = getCapabilities(dynamicRange);
return capabilities == null ? Quality.NONE : capabilities.findHighestSupportedQualityFor(
size);
}
- @NonNull
- private static Set<DynamicRange> getCandidateDynamicRanges(
- @NonNull EncoderProfilesProvider provider) {
- Set<DynamicRange> dynamicRanges = new HashSet<>();
- for (Quality quality : Quality.getSortedQualities()) {
- int qualityValue = ((Quality.ConstantQuality) quality).getValue();
- EncoderProfilesProxy encoderProfiles = provider.getAll(qualityValue);
-
- if (encoderProfiles != null) {
- for (VideoProfileProxy videoProfile : encoderProfiles.getVideoProfiles()) {
- Integer format = VP_TO_DR_FORMAT_MAP.get(videoProfile.getHdrFormat());
- if (format != null) {
- dynamicRanges.add(new DynamicRange(format, videoProfile.getBitDepth()));
- }
- }
- }
+ @Nullable
+ private CapabilitiesByQuality getCapabilities(@NonNull DynamicRange dynamicRange) {
+ if (isFullySpecified(dynamicRange)) {
+ return mCapabilitiesMapForFullySpecifiedDynamicRange.get(dynamicRange);
}
- dynamicRanges.add(SDR);
- dynamicRanges.add(HDR_UNSPECIFIED_10_BIT);
-
- return dynamicRanges;
- }
-
- private static boolean isDynamicRangeSupported(@NonNull DynamicRange dynamicRange,
- @NonNull CameraInfoInternal cameraInfoInternal) {
- Set<DynamicRange> supportedDynamicRanges = cameraInfoInternal.getSupportedDynamicRanges();
- if (supportedDynamicRanges.contains(dynamicRange)) {
- return true;
+ // Handle dynamic range that is not fully specified.
+ if (mCapabilitiesMapForNonFullySpecifiedDynamicRange.containsKey(dynamicRange)) {
+ return mCapabilitiesMapForNonFullySpecifiedDynamicRange.get(dynamicRange);
} else {
- return dynamicRange.equals(HDR_UNSPECIFIED_10_BIT) && contains10BitHdrDynamicRange(
- supportedDynamicRanges);
+ CapabilitiesByQuality capabilities =
+ generateCapabilitiesForNonFullySpecifiedDynamicRange(dynamicRange);
+ mCapabilitiesMapForNonFullySpecifiedDynamicRange.put(dynamicRange, capabilities);
+ return capabilities;
}
}
- private static boolean contains10BitHdrDynamicRange(@NonNull Set<DynamicRange> dynamicRanges) {
- for (DynamicRange dynamicRange : dynamicRanges) {
- if (dynamicRange.getFormat() != DynamicRange.FORMAT_SDR
- && dynamicRange.getBitDepth() == DynamicRange.BIT_DEPTH_10_BIT) {
- return true;
- }
- }
-
- return false;
- }
-
private static boolean isHlg10SupportedByCamera(
@NonNull CameraInfoInternal cameraInfoInternal) {
Set<DynamicRange> dynamicRanges = cameraInfoInternal.getSupportedDynamicRanges();
@@ -252,6 +223,78 @@
return false;
}
+ @Nullable
+ private CapabilitiesByQuality generateCapabilitiesForNonFullySpecifiedDynamicRange(
+ @NonNull DynamicRange dynamicRange) {
+ if (!canResolve(dynamicRange, getSupportedDynamicRanges())) {
+ return null;
+ }
+
+ // Filter video profiles to include only the profiles match with the target dynamic
+ // range.
+ EncoderProfilesProvider constrainedProvider =
+ new DynamicRangeMatchedEncoderProfilesProvider(mProfilesProvider, dynamicRange);
+ return new CapabilitiesByQuality(constrainedProvider);
+ }
+
+ /**
+ * Returns {@code true} if the test dynamic range can resolve to the fully specified dynamic
+ * range set.
+ *
+ * <p>A range can resolve if test fields are unspecified and appropriately match the fields
+ * of the fully specified dynamic range, or the test fields exactly match the fields of
+ * the fully specified dynamic range.
+ */
+ private static boolean canResolve(@NonNull DynamicRange dynamicRangeToTest,
+ @NonNull Set<DynamicRange> fullySpecifiedDynamicRanges) {
+ if (isFullySpecified(dynamicRangeToTest)) {
+ return fullySpecifiedDynamicRanges.contains(dynamicRangeToTest);
+ } else {
+ for (DynamicRange fullySpecifiedDynamicRange : fullySpecifiedDynamicRanges) {
+ if (canMatchBitDepth(dynamicRangeToTest, fullySpecifiedDynamicRange)
+ && canMatchFormat(dynamicRangeToTest, fullySpecifiedDynamicRange)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ private static boolean canMatchBitDepth(@NonNull DynamicRange dynamicRangeToTest,
+ @NonNull DynamicRange fullySpecifiedDynamicRange) {
+ Preconditions.checkState(isFullySpecified(fullySpecifiedDynamicRange), "Fully specified "
+ + "range is not actually fully specified.");
+ if (dynamicRangeToTest.getBitDepth() == BIT_DEPTH_UNSPECIFIED) {
+ return true;
+ }
+
+ return dynamicRangeToTest.getBitDepth() == fullySpecifiedDynamicRange.getBitDepth();
+ }
+
+ private static boolean canMatchFormat(@NonNull DynamicRange dynamicRangeToTest,
+ @NonNull DynamicRange fullySpecifiedDynamicRange) {
+ Preconditions.checkState(isFullySpecified(fullySpecifiedDynamicRange), "Fully specified "
+ + "range is not actually fully specified.");
+ int formatToTest = dynamicRangeToTest.getFormat();
+ if (formatToTest == FORMAT_UNSPECIFIED) {
+ return true;
+ }
+
+ int fullySpecifiedFormat = fullySpecifiedDynamicRange.getFormat();
+ if (formatToTest == FORMAT_HDR_UNSPECIFIED && fullySpecifiedFormat != FORMAT_SDR) {
+ return true;
+ }
+
+ return formatToTest == fullySpecifiedFormat;
+ }
+
+ private static boolean isFullySpecified(@NonNull DynamicRange dynamicRange) {
+ return dynamicRange.getFormat() != FORMAT_UNSPECIFIED
+ && dynamicRange.getFormat() != FORMAT_HDR_UNSPECIFIED
+ && dynamicRange.getBitDepth() != BIT_DEPTH_UNSPECIFIED;
+ }
+
/**
* This class implements the video capabilities query logic related to quality and resolution.
*/
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
index 8c83205..8d75923 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
@@ -17,7 +17,6 @@
package androidx.camera.video;
import static androidx.camera.core.CameraEffect.VIDEO_CAPTURE;
-import static androidx.camera.core.DynamicRange.SDR;
import static androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_DYNAMIC_RANGE;
import static androidx.camera.core.impl.ImageOutputConfig.OPTION_CUSTOM_ORDERED_RESOLUTIONS;
@@ -29,6 +28,7 @@
import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ROTATION;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAMERA_SELECTOR;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_CONFIG_UNPACKER;
+import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_DEFAULT_CAPTURE_CONFIG;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_DEFAULT_SESSION_CONFIG;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_HIGH_RESOLUTION_DISABLED;
@@ -91,6 +91,7 @@
import androidx.camera.core.impl.Config;
import androidx.camera.core.impl.ConfigProvider;
import androidx.camera.core.impl.DeferrableSurface;
+import androidx.camera.core.impl.ImageInputConfig;
import androidx.camera.core.impl.ImageOutputConfig;
import androidx.camera.core.impl.ImageOutputConfig.RotationValue;
import androidx.camera.core.impl.MutableConfig;
@@ -219,7 +220,8 @@
*/
@NonNull
public static <T extends VideoOutput> VideoCapture<T> withOutput(@NonNull T videoOutput) {
- return new VideoCapture.Builder<>(Preconditions.checkNotNull(videoOutput)).build();
+ return new VideoCapture.Builder<>(Preconditions.checkNotNull(videoOutput)).setCaptureType(
+ UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE).build();
}
/**
@@ -424,6 +426,31 @@
}
/**
+ * Returns the dynamic range.
+ *
+ * <p>The dynamic range is set by {@link VideoCapture.Builder#setDynamicRange(DynamicRange)}.
+ * If the dynamic range set is not a fully defined dynamic range, such as
+ * {@link DynamicRange#HDR_UNSPECIFIED_10_BIT}, then it will be returned just as provided,
+ * and will not be returned as a fully defined dynamic range.
+ *
+ * <p>If the dynamic range was not provided to
+ * {@link VideoCapture.Builder#setDynamicRange(DynamicRange)}, this will return the default of
+ * {@link DynamicRange#SDR}
+ *
+ * @return the dynamic range set for this {@code VideoCapture} use case.
+ */
+ // Internal implementation note: this method should not be used to retrieve the dynamic range
+ // that will be sent to the VideoOutput. That should always be retrieved from the StreamSpec
+ // since that will be the final DynamicRange chosen by the camera based on other use case
+ // combinations.
+ @RestrictTo(Scope.LIBRARY)
+ @NonNull
+ public DynamicRange getDynamicRange() {
+ return getCurrentConfig().hasDynamicRange() ? getCurrentConfig().getDynamicRange() :
+ Defaults.DEFAULT_DYNAMIC_RANGE;
+ }
+
+ /**
* {@inheritDoc}
*
*/
@@ -497,7 +524,7 @@
public UseCaseConfig<?> getDefaultConfig(boolean applyDefaultConfig,
@NonNull UseCaseConfigFactory factory) {
Config captureConfig = factory.getConfig(
- UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE,
+ DEFAULT_CONFIG.getConfig().getCaptureType(),
ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY);
if (applyDefaultConfig) {
@@ -770,10 +797,18 @@
static final Range<Integer> DEFAULT_FPS_RANGE = new Range<>(30, 30);
+ /**
+ * Explicitly setting the default dynamic range to SDR (rather than UNSPECIFIED) means
+ * VideoCapture won't inherit dynamic ranges from other use cases.
+ */
+ static final DynamicRange DEFAULT_DYNAMIC_RANGE = DynamicRange.SDR;
+
static {
Builder<?> builder = new Builder<>(DEFAULT_VIDEO_OUTPUT)
.setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
- .setVideoEncoderInfoFinder(DEFAULT_VIDEO_ENCODER_INFO_FINDER);
+ .setVideoEncoderInfoFinder(DEFAULT_VIDEO_ENCODER_INFO_FINDER)
+ .setDynamicRange(DEFAULT_DYNAMIC_RANGE)
+ .setCaptureType(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE);
DEFAULT_CONFIG = builder.getUseCaseConfig();
}
@@ -1209,12 +1244,12 @@
Preconditions.checkArgument(mediaSpec != null,
"Unable to update target resolution by null MediaSpec.");
- DynamicRange dynamicRange = Preconditions.checkNotNull(
- builder.getMutableConfig().retrieveOption(OPTION_INPUT_DYNAMIC_RANGE, SDR));
+ DynamicRange requestedDynamicRange = getDynamicRange();
VideoCapabilities videoCapabilities = getVideoCapabilities(cameraInfo);
// Get supported qualities.
- List<Quality> supportedQualities = videoCapabilities.getSupportedQualities(dynamicRange);
+ List<Quality> supportedQualities = videoCapabilities.getSupportedQualities(
+ requestedDynamicRange);
if (supportedQualities.isEmpty()) {
// When the device does not have any supported quality, even the most flexible
// QualitySelector such as QualitySelector.from(Quality.HIGHEST), still cannot
@@ -1238,7 +1273,8 @@
// Get corresponded resolutions for the target aspect ratio.
int aspectRatio = videoSpec.getAspectRatio();
- Map<Quality, Size> sizeMap = getQualityToResolutionMap(videoCapabilities, dynamicRange);
+ Map<Quality, Size> sizeMap = getQualityToResolutionMap(videoCapabilities,
+ requestedDynamicRange);
QualityRatioToResolutionsTable qualityRatioTable = new QualityRatioToResolutionsTable(
cameraInfo.getSupportedResolutions(getImageFormat()), sizeMap);
List<Size> customOrderedResolutions = new ArrayList<>();
@@ -1325,7 +1361,8 @@
@SuppressWarnings("ObjectToString")
public static final class Builder<T extends VideoOutput> implements
UseCaseConfig.Builder<VideoCapture<T>, VideoCaptureConfig<T>, Builder<T>>,
- ImageOutputConfig.Builder<Builder<T>>, ThreadConfig.Builder<Builder<T>> {
+ ImageOutputConfig.Builder<Builder<T>>, ImageInputConfig.Builder<Builder<T>>,
+ ThreadConfig.Builder<Builder<T>> {
private final MutableOptionsBundle mMutableConfig;
/** Creates a new Builder object. */
@@ -1590,6 +1627,41 @@
return this;
}
+ // Implementations of ImageInputConfig.Builder default methods
+
+ /**
+ * Sets the {@link DynamicRange}.
+ *
+ * <p>The dynamic range specifies how the range of colors, highlights and shadows that
+ * are captured by the video producer are displayed on a display. Some dynamic ranges will
+ * allow the video to make full use of the extended range of brightness of a display when
+ * the video is played back.
+ *
+ * <p>The supported dynamic ranges for video capture depend on the capabilities of the
+ * camera and the {@link VideoOutput}. The supported dynamic ranges can normally be
+ * queried through the specific video output. For example, the available dynamic
+ * ranges for the {@link Recorder} video output can be queried through
+ * the {@link androidx.camera.video.VideoCapabilities} returned by
+ * {@link Recorder#getVideoCapabilities(CameraInfo)} via
+ * {@link androidx.camera.video.VideoCapabilities#getSupportedDynamicRanges()}.
+ *
+ * <p>It is possible to choose a high dynamic range (HDR) with unspecified encoding by
+ * providing {@link DynamicRange#HDR_UNSPECIFIED_10_BIT}.
+ *
+ * <p>If the dynamic range is not provided, the returned video capture use case will use
+ * a default of {@link DynamicRange#SDR}.
+ *
+ * @return The current Builder.
+ * @see DynamicRange
+ */
+ @RestrictTo(Scope.LIBRARY)
+ @NonNull
+ @Override
+ public Builder<T> setDynamicRange(@NonNull DynamicRange dynamicRange) {
+ getMutableConfig().insertOption(OPTION_INPUT_DYNAMIC_RANGE, dynamicRange);
+ return this;
+ }
+
// Implementations of ThreadConfig.Builder default methods
/**
@@ -1703,5 +1775,13 @@
getMutableConfig().insertOption(OPTION_TARGET_FRAME_RATE, targetFrameRate);
return this;
}
+
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ @Override
+ public Builder<T> setCaptureType(@NonNull UseCaseConfigFactory.CaptureType captureType) {
+ getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
+ return this;
+ }
}
}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/CameraUseInconsistentTimebaseQuirk.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/CameraUseInconsistentTimebaseQuirk.java
index 63e0121..0dc036e 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/CameraUseInconsistentTimebaseQuirk.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/CameraUseInconsistentTimebaseQuirk.java
@@ -24,15 +24,18 @@
import androidx.camera.video.internal.workaround.VideoTimebaseConverter;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* <p>QuirkSummary
- * Bug Id: 197805856
- * Description: Quirk that denotes some Samsung devices use inconsistent timebase for camera
- * frame.
- * Device(s): Some Samsung devices
+ * Bug Id: 197805856, 280121263
+ * Description: Quirk that denotes some devices use a timebase for camera frames that is
+ * different than what is reported by
+ * {@link android.hardware.camera2.CameraCharacteristics
+ * #SENSOR_INFO_TIMESTAMP_SOURCE}. This can cause A/V sync issues.
+ * Device(s): Some Samsung devices and devices running on certain Qualcomm SoCs
* @see VideoTimebaseConverter
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
@@ -43,7 +46,20 @@
"qcom"
));
+ private static final Set<String> BUILD_SOC_MODEL_SET = new HashSet<>(Collections.singletonList(
+ "sm6375"
+ ));
+
static boolean load() {
+ return usesAffectedSoc() || isAffectedSamsungDevice();
+ }
+
+ private static boolean usesAffectedSoc() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
+ && BUILD_SOC_MODEL_SET.contains(Build.SOC_MODEL.toLowerCase());
+ }
+
+ private static boolean isAffectedSamsungDevice() {
return "SAMSUNG".equalsIgnoreCase(Build.BRAND)
&& BUILD_HARDWARE_SET.contains(Build.HARDWARE.toLowerCase());
}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/utils/DynamicRangeUtil.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/utils/DynamicRangeUtil.java
index 8057fdf..e079aba 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/utils/DynamicRangeUtil.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/utils/DynamicRangeUtil.java
@@ -31,6 +31,7 @@
import static androidx.camera.core.DynamicRange.FORMAT_HDR_UNSPECIFIED;
import static androidx.camera.core.DynamicRange.FORMAT_HLG;
import static androidx.camera.core.DynamicRange.FORMAT_SDR;
+import static androidx.camera.core.DynamicRange.FORMAT_UNSPECIFIED;
import static androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy.BIT_DEPTH_10;
import static androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy.BIT_DEPTH_8;
@@ -65,6 +66,8 @@
new HashSet<>(asList(BIT_DEPTH_8, BIT_DEPTH_10)));
// DynamicRange format to VideoProfile HDR format.
+ DR_TO_VP_FORMAT_MAP.put(FORMAT_UNSPECIFIED, new HashSet<>(asList(HDR_NONE, HDR_HLG,
+ HDR_HDR10, HDR_HDR10PLUS, HDR_DOLBY_VISION)));
DR_TO_VP_FORMAT_MAP.put(FORMAT_SDR, new HashSet<>(singletonList(HDR_NONE)));
DR_TO_VP_FORMAT_MAP.put(FORMAT_HDR_UNSPECIFIED,
new HashSet<>(asList(HDR_HLG, HDR_HDR10, HDR_HDR10PLUS, HDR_DOLBY_VISION)));
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/RecorderVideoCapabilitiesTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/RecorderVideoCapabilitiesTest.kt
index fb598f9..f917aca 100644
--- a/camera/camera-video/src/test/java/androidx/camera/video/RecorderVideoCapabilitiesTest.kt
+++ b/camera/camera-video/src/test/java/androidx/camera/video/RecorderVideoCapabilitiesTest.kt
@@ -21,9 +21,16 @@
import android.util.Size
import androidx.camera.core.DynamicRange
import androidx.camera.core.DynamicRange.BIT_DEPTH_10_BIT
+import androidx.camera.core.DynamicRange.BIT_DEPTH_8_BIT
+import androidx.camera.core.DynamicRange.BIT_DEPTH_UNSPECIFIED
+import androidx.camera.core.DynamicRange.FORMAT_DOLBY_VISION
+import androidx.camera.core.DynamicRange.FORMAT_HDR10
+import androidx.camera.core.DynamicRange.FORMAT_HDR_UNSPECIFIED
import androidx.camera.core.DynamicRange.FORMAT_HLG
+import androidx.camera.core.DynamicRange.FORMAT_UNSPECIFIED
import androidx.camera.core.DynamicRange.HDR_UNSPECIFIED_10_BIT
import androidx.camera.core.DynamicRange.SDR
+import androidx.camera.core.DynamicRange.UNSPECIFIED
import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy
import androidx.camera.testing.EncoderProfilesUtil.PROFILES_2160P
import androidx.camera.testing.EncoderProfilesUtil.PROFILES_720P
@@ -48,6 +55,11 @@
import org.robolectric.annotation.internal.DoNotInstrument
private val HLG10 = DynamicRange(FORMAT_HLG, BIT_DEPTH_10_BIT)
+private val HDR10 = DynamicRange(FORMAT_HDR10, BIT_DEPTH_10_BIT)
+private val UNSPECIFIED_8_BIT = DynamicRange(FORMAT_UNSPECIFIED, BIT_DEPTH_8_BIT)
+private val UNSPECIFIED_10_BIT = DynamicRange(FORMAT_UNSPECIFIED, BIT_DEPTH_10_BIT)
+private val HDR_UNSPECIFIED = DynamicRange(FORMAT_HDR_UNSPECIFIED, BIT_DEPTH_UNSPECIFIED)
+private val DOLBY_VISION_UNSPECIFIED = DynamicRange(FORMAT_DOLBY_VISION, BIT_DEPTH_UNSPECIFIED)
@RunWith(RobolectricTestRunner::class)
@DoNotInstrument
@@ -80,6 +92,55 @@
}
@Test
+ fun hasSupportedQualities_sdr() {
+ assertThat(videoCapabilities.getSupportedQualities(SDR)).containsExactly(HD, UHD)
+ }
+
+ @Test
+ fun hasSupportedQualities_hlg10() {
+ assertThat(videoCapabilities.getSupportedQualities(HLG10)).containsExactly(HD, UHD)
+ }
+
+ @Test
+ fun hasSupportedQualities_hdr10() {
+ assertThat(videoCapabilities.getSupportedQualities(HDR10)).isEmpty()
+ }
+
+ @Test
+ fun hasSupportedQualities_unspecified() {
+ assertThat(videoCapabilities.getSupportedQualities(UNSPECIFIED)).containsExactly(HD, UHD)
+ }
+
+ @Test
+ fun hasSupportedQualities_hdrUnspecified() {
+ assertThat(videoCapabilities.getSupportedQualities(HDR_UNSPECIFIED))
+ .containsExactly(HD, UHD)
+ }
+
+ @Test
+ fun hasSupportedQualities_hdrUnspecified10Bit() {
+ assertThat(videoCapabilities.getSupportedQualities(HDR_UNSPECIFIED_10_BIT))
+ .containsExactly(HD, UHD)
+ }
+
+ @Test
+ fun hasSupportedQualities_unspecified8Bit() {
+ assertThat(videoCapabilities.getSupportedQualities(UNSPECIFIED_8_BIT))
+ .containsExactly(HD, UHD)
+ }
+
+ @Test
+ fun hasSupportedQualities_unspecified10Bit() {
+ assertThat(videoCapabilities.getSupportedQualities(UNSPECIFIED_10_BIT))
+ .containsExactly(HD, UHD)
+ }
+
+ @Test
+ fun hasSupportedQualities_dolbyVisionUnspecified() {
+ assertThat(videoCapabilities.getSupportedQualities(DOLBY_VISION_UNSPECIFIED)).isEmpty()
+ }
+
+ @Test
fun isQualitySupported_sdr() {
assertThat(videoCapabilities.isQualitySupported(HIGHEST, SDR)).isTrue()
assertThat(videoCapabilities.isQualitySupported(LOWEST, SDR)).isTrue()
@@ -90,6 +151,16 @@
}
@Test
+ fun isQualitySupported_unspecified() {
+ assertThat(videoCapabilities.isQualitySupported(HIGHEST, UNSPECIFIED)).isTrue()
+ assertThat(videoCapabilities.isQualitySupported(LOWEST, UNSPECIFIED)).isTrue()
+ assertThat(videoCapabilities.isQualitySupported(UHD, UNSPECIFIED)).isTrue()
+ assertThat(videoCapabilities.isQualitySupported(FHD, UNSPECIFIED)).isFalse()
+ assertThat(videoCapabilities.isQualitySupported(HD, UNSPECIFIED)).isTrue()
+ assertThat(videoCapabilities.isQualitySupported(SD, UNSPECIFIED)).isFalse()
+ }
+
+ @Test
fun isQualitySupported_hlg10WithBackupProfile() {
assertThat(videoCapabilities.isQualitySupported(HIGHEST, HLG10)).isTrue()
assertThat(videoCapabilities.isQualitySupported(LOWEST, HLG10)).isTrue()
@@ -100,7 +171,7 @@
}
@Test
- fun isQualitySupported_hdrUnspecifiedWithBackupProfile() {
+ fun isQualitySupported_hdrUnspecified10BitWithBackupProfile() {
assertThat(videoCapabilities.isQualitySupported(HIGHEST, HDR_UNSPECIFIED_10_BIT)).isTrue()
assertThat(videoCapabilities.isQualitySupported(LOWEST, HDR_UNSPECIFIED_10_BIT)).isTrue()
assertThat(videoCapabilities.isQualitySupported(UHD, HDR_UNSPECIFIED_10_BIT)).isTrue()
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt
index 93f2245..02ef374 100644
--- a/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt
+++ b/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt
@@ -45,6 +45,8 @@
import androidx.camera.core.CameraSelector.LENS_FACING_FRONT
import androidx.camera.core.CameraXConfig
import androidx.camera.core.DynamicRange
+import androidx.camera.core.DynamicRange.BIT_DEPTH_10_BIT
+import androidx.camera.core.DynamicRange.FORMAT_HLG
import androidx.camera.core.MirrorMode.MIRROR_MODE_OFF
import androidx.camera.core.MirrorMode.MIRROR_MODE_ON
import androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
@@ -747,6 +749,20 @@
}
@Test
+ fun defaultDynamicRangeIsSdr() {
+ val videoCapture = createVideoCapture()
+ assertThat(videoCapture.dynamicRange).isEqualTo(DynamicRange.SDR)
+ }
+
+ @Test
+ fun canSetDynamicRange() {
+ val videoCapture = createVideoCapture(
+ dynamicRange = DynamicRange.HDR_UNSPECIFIED_10_BIT
+ )
+ assertThat(videoCapture.dynamicRange).isEqualTo(DynamicRange.HDR_UNSPECIFIED_10_BIT)
+ }
+
+ @Test
fun defaultMirrorModeIsOff() {
val videoCapture = createVideoCapture()
assertThat(videoCapture.mirrorMode).isEqualTo(MIRROR_MODE_OFF)
@@ -1017,6 +1033,16 @@
)
}
+ @Test
+ fun suggestedStreamSpecDynamicRange_isPropagatedToSurfaceRequest() {
+ // This ensures the dynamic range set on the VideoCapture.Builder is not just directly
+ // propagated to the SurfaceRequest. It should come from the StreamSpec.
+ testSurfaceRequestContainsExpected(
+ requestedDynamicRange = DynamicRange.HDR_UNSPECIFIED_10_BIT,
+ expectedDynamicRange = DynamicRange(FORMAT_HLG, BIT_DEPTH_10_BIT)
+ )
+ }
+
private fun testSetTargetRotation_transformationInfoUpdated(
lensFacing: Int = LENS_FACING_BACK,
sensorRotationDegrees: Int = 0,
@@ -1256,14 +1282,17 @@
cropRect: Rect? = null,
expectedCropRect: Rect? = null,
targetFrameRate: Range<Int>? = null,
- expectedFrameRate: Range<Int> = SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED
+ expectedFrameRate: Range<Int> = SurfaceRequest.FRAME_RATE_RANGE_UNSPECIFIED,
+ requestedDynamicRange: DynamicRange? = null,
+ expectedDynamicRange: DynamicRange? = null
) {
// Arrange.
setupCamera()
createCameraUseCaseAdapter()
setSuggestedStreamSpec(
quality,
- expectedFrameRate = expectedFrameRate
+ expectedFrameRate = expectedFrameRate,
+ dynamicRange = expectedDynamicRange
)
var surfaceRequest: SurfaceRequest? = null
val videoOutput = createVideoOutput(
@@ -1275,7 +1304,8 @@
val videoCapture = createVideoCapture(
videoOutput,
videoEncoderInfoFinder = { videoEncoderInfo },
- targetFrameRate = targetFrameRate
+ targetFrameRate = targetFrameRate,
+ dynamicRange = requestedDynamicRange
)
cropRect?.let {
@@ -1296,6 +1326,10 @@
if (expectedFrameRate != StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED) {
assertThat(surfaceRequest!!.expectedFrameRate).isEqualTo(expectedFrameRate)
}
+
+ expectedDynamicRange?.let {
+ assertThat(surfaceRequest!!.dynamicRange).isEqualTo(expectedDynamicRange)
+ }
}
private fun assertCustomOrderedResolutions(
@@ -1393,6 +1427,7 @@
mirrorMode: Int? = null,
targetResolution: Size? = null,
targetFrameRate: Range<Int>? = null,
+ dynamicRange: DynamicRange? = null,
videoEncoderInfoFinder: Function<VideoEncoderConfig, VideoEncoderInfo> =
Function { createVideoEncoderInfo() },
): VideoCapture<VideoOutput> = VideoCapture.Builder(videoOutput)
@@ -1402,6 +1437,7 @@
mirrorMode?.let { setMirrorMode(it) }
targetResolution?.let { setTargetResolution(it) }
targetFrameRate?.let { setTargetFrameRate(it) }
+ dynamicRange?.let { setDynamicRange(it) }
setVideoEncoderInfoFinder(videoEncoderInfoFinder)
}.build()
@@ -1428,11 +1464,14 @@
private fun setSuggestedStreamSpec(
quality: Quality,
- expectedFrameRate: Range<Int> = StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED
+ expectedFrameRate: Range<Int> = StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED,
+ dynamicRange: DynamicRange? = null
) {
setSuggestedStreamSpec(
- StreamSpec.builder(CAMERA_0_QUALITY_SIZE[quality]!!)
- .setExpectedFrameRateRange(expectedFrameRate).build()
+ StreamSpec.builder(CAMERA_0_QUALITY_SIZE[quality]!!).apply {
+ setExpectedFrameRateRange(expectedFrameRate)
+ dynamicRange?.let { setDynamicRange(dynamicRange) }
+ }.build()
)
}
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/transform/FileTransformFactoryDeviceTest.kt b/camera/camera-view/src/androidTest/java/androidx/camera/view/transform/FileTransformFactoryDeviceTest.kt
index 62e859f..aee04dc 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/transform/FileTransformFactoryDeviceTest.kt
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/transform/FileTransformFactoryDeviceTest.kt
@@ -22,6 +22,7 @@
import android.graphics.Bitmap
import android.media.ExifInterface
import android.net.Uri
+import android.os.Environment
import android.provider.MediaStore
import androidx.camera.core.impl.utils.Exif
import androidx.test.core.app.ApplicationProvider.getApplicationContext
@@ -30,13 +31,13 @@
import androidx.test.filters.SdkSuppress
import androidx.test.rule.GrantPermissionRule
import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import java.io.File
-import java.io.FileInputStream
-import java.io.FileOutputStream
private const val WIDTH = 80
private const val HEIGHT = 60
@@ -128,6 +129,9 @@
}
private fun createMediaStoreImage(): Uri {
+ // Ensure the folder of MediaStore.Images.Media is created.
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)?.mkdirs()
+
val contentValues = ContentValues()
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
val uri = contentResolver.insert(
diff --git a/camera/integration-tests/avsynctestapp/build.gradle b/camera/integration-tests/avsynctestapp/build.gradle
index 3fa42bc..bba9ab2 100644
--- a/camera/integration-tests/avsynctestapp/build.gradle
+++ b/camera/integration-tests/avsynctestapp/build.gradle
@@ -52,8 +52,8 @@
implementation("androidx.compose.material:material:$compose_version")
implementation("androidx.compose.ui:ui:$compose_version")
implementation("androidx.compose.ui:ui-tooling-preview:$compose_version")
- implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1")
- implementation(project(":lifecycle:lifecycle-viewmodel"))
+ implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
+ implementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
compileOnly(libs.kotlinCompiler)
@@ -62,7 +62,7 @@
testImplementation(libs.junit)
testImplementation(libs.truth)
androidTestImplementation(project(":camera:camera-testing"))
- androidTestImplementation(project(":lifecycle:lifecycle-viewmodel"))
+ androidTestImplementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
androidTestImplementation(libs.kotlinCoroutinesTest)
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testRules)
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 920268e..09c42a4 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
@@ -85,11 +85,13 @@
override fun onResume() {
super.onResume()
Log.i("CXCP-App", "Activity onResume")
+ currentCamera?.resume()
}
override fun onPause() {
super.onPause()
Log.i("CXCP-App", "Activity onPause")
+ currentCamera?.pause()
}
override fun onStop() {
@@ -139,7 +141,8 @@
for (id in cameras) {
val metadata = cameraPipe.cameras().getCameraMetadata(id)
if (metadata != null && metadata[CameraCharacteristics.LENS_FACING] ==
- CameraCharacteristics.LENS_FACING_BACK) {
+ CameraCharacteristics.LENS_FACING_BACK
+ ) {
return id
}
}
diff --git a/camera/integration-tests/camerapipetestapp/src/main/java/androidx/camera/integration/camera2/pipe/SimpleCamera.kt b/camera/integration-tests/camerapipetestapp/src/main/java/androidx/camera/integration/camera2/pipe/SimpleCamera.kt
index e9a7863..e852046 100644
--- a/camera/integration-tests/camerapipetestapp/src/main/java/androidx/camera/integration/camera2/pipe/SimpleCamera.kt
+++ b/camera/integration-tests/camerapipetestapp/src/main/java/androidx/camera/integration/camera2/pipe/SimpleCamera.kt
@@ -332,6 +332,14 @@
cameraGraph.start()
}
+ fun resume() {
+ cameraGraph.isForeground = true
+ }
+
+ fun pause() {
+ cameraGraph.isForeground = false
+ }
+
fun stop() {
Log.i("CXCP-App", "Stopping $cameraGraph")
cameraGraph.stop()
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/BasicUITest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/BasicUITest.kt
index 26d932d..2fb1a60 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/BasicUITest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/BasicUITest.kt
@@ -19,6 +19,7 @@
import android.Manifest
import android.content.Context
import android.content.Intent
+import android.os.Build
import androidx.camera.camera2.Camera2Config
import androidx.camera.camera2.pipe.integration.CameraPipeConfig
import androidx.camera.lifecycle.ProcessCameraProvider
@@ -38,7 +39,7 @@
import java.util.concurrent.TimeUnit
import leakcanary.DetectLeaksAfterTestSuccess
import org.junit.After
-import org.junit.Assume
+import org.junit.Assume.assumeFalse
import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Rule
@@ -94,7 +95,12 @@
@Before
fun setUp() {
- Assume.assumeTrue(CameraUtil.deviceHasCamera())
+ assumeTrue(CameraUtil.deviceHasCamera())
+ assumeFalse(
+ "See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
+ " the test failure.",
+ Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30
+ )
CoreAppTestUtil.assumeCompatibleDevice()
// Use the natural orientation throughout these tests to ensure the activity isn't
// recreated unexpectedly. This will also freeze the sensors until
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.kt
index 0c9a82a..912d748 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.kt
@@ -100,6 +100,9 @@
"Ignore Cuttlefish",
Build.MODEL.contains("Cuttlefish")
)
+ Assume.assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
+ " the test failure.",
+ Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30)
Assume.assumeTrue(CameraUtil.deviceHasCamera())
CoreAppTestUtil.assumeCompatibleDevice()
// Clear the device UI and check if there is no dialog or lock screen on the top of the
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FocusMeteringDeviceTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FocusMeteringDeviceTest.kt
index 056a07e..8aceeb4 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FocusMeteringDeviceTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FocusMeteringDeviceTest.kt
@@ -44,7 +44,6 @@
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.hamcrest.CoreMatchers.equalTo
@@ -140,11 +139,6 @@
cameraProvider.shutdown()[10, TimeUnit.SECONDS]
}
}
-
- if (selectorName == "front" && implName == CameraPipeConfig::class.simpleName) {
- // TODO(b/264332446): Replace this delay with some API like closeAll() once available
- delay(5000)
- }
}
@Test
@@ -381,8 +375,8 @@
)
cameraCharacteristics?.run {
(if (flags.hasFlag(FLAG_AF)) (get(CONTROL_MAX_REGIONS_AF)!! > 0) else false) ||
- (if (flags.hasFlag(FLAG_AE)) (get(CONTROL_MAX_REGIONS_AE)!! > 0) else false) ||
- (if (flags.hasFlag(FLAG_AWB)) (get(CONTROL_MAX_REGIONS_AWB)!! > 0) else false)
+ (if (flags.hasFlag(FLAG_AE)) (get(CONTROL_MAX_REGIONS_AE)!! > 0) else false) ||
+ (if (flags.hasFlag(FLAG_AWB)) (get(CONTROL_MAX_REGIONS_AWB)!! > 0) else false)
} ?: false
} catch (e: Exception) {
false
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
index 8b40b54..5402bfe 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
@@ -71,6 +71,8 @@
import androidx.camera.testing.SurfaceTextureProvider
import androidx.camera.testing.fakes.FakeLifecycleOwner
import androidx.camera.testing.fakes.FakeSessionProcessor
+import androidx.camera.video.Recorder
+import androidx.camera.video.VideoCapture
import androidx.core.content.ContextCompat
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.LargeTest
@@ -96,6 +98,7 @@
import org.junit.Assume.assumeNotNull
import org.junit.Assume.assumeTrue
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
@@ -147,6 +150,9 @@
@Before
fun setUp(): Unit = runBlocking {
+ assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
+ " the test failure.",
+ Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30)
assumeTrue(CameraUtil.hasCameraWithLensFacing(BACK_LENS_FACING))
createDefaultPictureFolderIfNotExist()
ProcessCameraProvider.configureInstance(cameraXConfig)
@@ -1574,6 +1580,48 @@
}
@Test
+ @Ignore("b/280379397")
+ fun unbindVideoCaptureWithoutStartingRecorder_imageCapturingShouldSuccess() = runBlocking {
+ // Arrange.
+ val imageCapture = ImageCapture.Builder().build()
+ val videoStreamReceived = CompletableDeferred<Boolean>()
+ val videoCapture = VideoCapture.Builder<Recorder>(Recorder.Builder().build()).also {
+ CameraPipeUtil.setCameraCaptureSessionCallback(
+ implName,
+ it,
+ object : CaptureCallback() {
+ override fun onCaptureCompleted(
+ session: CameraCaptureSession,
+ request: CaptureRequest,
+ result: TotalCaptureResult
+ ) {
+ videoStreamReceived.complete(true)
+ }
+ })
+ }.build()
+
+ withContext(Dispatchers.Main) {
+ cameraProvider.bindToLifecycle(
+ fakeLifecycleOwner, BACK_SELECTOR, imageCapture, videoCapture
+ )
+ }
+
+ assertWithMessage("VideoCapture doesn't start").that(
+ videoStreamReceived.awaitWithTimeoutOrNull()
+ ).isTrue()
+
+ // Act.
+ val callback = FakeImageCaptureCallback(capturesCount = 1)
+ withContext(Dispatchers.Main) {
+ cameraProvider.unbind(videoCapture)
+ imageCapture.takePicture(mainExecutor, callback)
+ }
+
+ // Assert.
+ callback.awaitCapturesAndAssert(capturedImagesCount = 1)
+ }
+
+ @Test
fun capturedImage_withHighResolutionEnabled_imageCaptureOnly() = runBlocking {
capturedImage_withHighResolutionEnabled()
}
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ZoomControlDeviceTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ZoomControlDeviceTest.kt
index 1b89972..7ed59c5 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ZoomControlDeviceTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ZoomControlDeviceTest.kt
@@ -56,7 +56,6 @@
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
@@ -134,11 +133,6 @@
cameraProvider.shutdown()[10, TimeUnit.SECONDS]
}
}
-
- if (selectorName == "front" && implName == CameraPipeConfig::class.simpleName) {
- // TODO(b/264332446): Replace this delay with some API like closeAll() once available
- delay(5000)
- }
}
@Test
@@ -186,7 +180,8 @@
*/
try {
cameraControl.setZoomRatio(maxZoomRatio + 1.0f)[5, TimeUnit.SECONDS]
- } catch (_: ExecutionException) {}
+ } catch (_: ExecutionException) {
+ }
assertThat(cameraInfo.zoomState.value?.zoomRatio).isEqualTo(2.0f)
}
@@ -213,7 +208,8 @@
*/
try {
cameraControl.setZoomRatio(minZoomRatio - 1.0f)[5, TimeUnit.SECONDS]
- } catch (_: ExecutionException) {}
+ } catch (_: ExecutionException) {
+ }
assertThat(cameraInfo.zoomState.value?.zoomRatio).isEqualTo(2.0f)
}
@@ -425,7 +421,8 @@
*/
try {
cameraControl.setLinearZoom(1.1f)[5, TimeUnit.SECONDS]
- } catch (_: ExecutionException) {}
+ } catch (_: ExecutionException) {
+ }
assertThat(cameraInfo.zoomState.value?.linearZoom).isEqualTo(0.5f)
}
@@ -448,7 +445,8 @@
*/
try {
cameraControl.setLinearZoom(-0.1f)[5, TimeUnit.SECONDS]
- } catch (_: ExecutionException) {}
+ } catch (_: ExecutionException) {
+ }
assertThat(cameraInfo.zoomState.value?.linearZoom).isEqualTo(0.5f)
}
@@ -761,8 +759,10 @@
private val failureException =
TimeoutException("Test doesn't complete after waiting for $captureCount frames.")
- @Volatile private var startReceiving = false
- @Volatile private var _verifyBlock: (
+ @Volatile
+ private var startReceiving = false
+ @Volatile
+ private var _verifyBlock: (
captureRequest: CaptureRequest,
captureResult: TotalCaptureResult
) -> Boolean = { _, _ -> false }
diff --git a/camera/integration-tests/coretestapp/src/main/cpp/opengl_renderer_jni.cpp b/camera/integration-tests/coretestapp/src/main/cpp/opengl_renderer_jni.cpp
index 8154dd9..9d94a395d 100644
--- a/camera/integration-tests/coretestapp/src/main/cpp/opengl_renderer_jni.cpp
+++ b/camera/integration-tests/coretestapp/src/main/cpp/opengl_renderer_jni.cpp
@@ -434,7 +434,7 @@
JNIEXPORT jboolean JNICALL
Java_androidx_camera_integration_core_OpenGLRenderer_renderTexture(
JNIEnv *env, jclass clazz, jlong context, jlong timestampNs,
- jfloatArray jmvpTransformArray, jboolean mvpDirty,jfloatArray jtexTransformArray) {
+ jfloatArray jmvpTransformArray, jboolean mvpDirty, jfloatArray jtexTransformArray) {
auto *nativeContext = reinterpret_cast<NativeContext *>(context);
// We use two triangles drawn with GL_TRIANGLE_STRIP to create the surface which will be
@@ -452,16 +452,16 @@
// +---------------+
// (-1,1) (1,1)
constexpr GLfloat vertices[] = {
- -1.0f, 1.0f, // Lower-left
- 1.0f, 1.0f, // Lower-right
+ -1.0f, 1.0f, // Lower-left
+ 1.0f, 1.0f, // Lower-right
-1.0f, -1.0f, // Upper-left (notice order here. We're drawing triangles, not a quad.)
- 1.0f, -1.0f // Upper-right
+ 1.0f, -1.0f // Upper-right
};
constexpr GLfloat texCoords[] = {
- 0.0f, 1.0f, // Upper-left
- 1.0f, 1.0f, // Upper-right
0.0f, 0.0f, // Lower-left
1.0f, 0.0f, // Lower-right
+ 0.0f, 1.0f, // Upper-left (order must match the vertices)
+ 1.0f, 1.0f, // Upper-right
};
GLint vertexComponents = 2;
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/OpenGLRenderer.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/OpenGLRenderer.java
index 2c3dd80..ccc905ae 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/OpenGLRenderer.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/OpenGLRenderer.java
@@ -16,6 +16,8 @@
package androidx.camera.integration.core;
+import static androidx.camera.core.impl.utils.TransformUtils.within360;
+
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
@@ -503,7 +505,7 @@
if (mHasCameraTransform) {
// If the Surface is connected to the camera, there is surface rotation encoded in
// the SurfaceTexture.
- return (mTextureRotationDegrees + mSurfaceRotationDegrees) % 360;
+ return within360((180 - mTextureRotationDegrees) + mSurfaceRotationDegrees);
} else {
// When the Surface is connected to an internal OpenGl renderer, the texture rotation
// is always 0. Use the rotation provided by SurfaceRequest instead.
@@ -553,7 +555,7 @@
private void updateModelTransform() {
// Remove the rotation to the device 'natural' orientation so our world space will be in
// sensor coordinates.
- Matrix.setRotateM(mTempMatrix, 0, mTextureRotationDegrees, 0.0f, 0.0f, 1.0f);
+ Matrix.setRotateM(mTempMatrix, 0, -(180 - mTextureRotationDegrees), 0.0f, 0.0f, 1.0f);
Matrix.setIdentityM(mTempMatrix, 16);
// Translate to the upper left corner of the quad so we are in buffer space
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureLockedOrientationTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureLockedOrientationTest.kt
index 7165a2e..b8d8429 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureLockedOrientationTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureLockedOrientationTest.kt
@@ -16,9 +16,11 @@
package androidx.camera.integration.uiwidgets.rotations
+import android.os.Build
import androidx.test.core.app.ActivityScenario
import androidx.test.filters.LargeTest
import org.junit.After
+import org.junit.Assume
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -54,6 +56,11 @@
@Before
fun before() {
+ Assume.assumeFalse(
+ "See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
+ " the test failure.",
+ Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30
+ )
setUp(lensFacing)
}
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt
index ffc5c97..1ff8e45 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt
@@ -76,6 +76,10 @@
"redmi note 8"
).contains(Build.MODEL.lowercase(Locale.US)) && rotation == Surface.ROTATION_180
)
+ Assume.assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
+ " the test failure.",
+ Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30)
+
setUp(lensFacing)
}
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureUnlockedOrientationTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureUnlockedOrientationTest.kt
index a0204db..aaedb05 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureUnlockedOrientationTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureUnlockedOrientationTest.kt
@@ -17,6 +17,7 @@
package androidx.camera.integration.uiwidgets.rotations
import android.app.Instrumentation
+import android.os.Build
import androidx.camera.core.CameraSelector
import androidx.camera.integration.uiwidgets.rotations.CameraActivity.Companion.IMAGE_CAPTURE_MODE_FILE
import androidx.camera.integration.uiwidgets.rotations.CameraActivity.Companion.IMAGE_CAPTURE_MODE_IN_MEMORY
@@ -25,6 +26,7 @@
import androidx.test.filters.LargeTest
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.After
+import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -87,6 +89,9 @@
@Before
fun before() {
+ assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
+ " the test failure.",
+ Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30)
setUp(lensFacing)
}
diff --git a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
index 610e9ae..b7d5960 100644
--- a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
+++ b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
@@ -60,6 +60,8 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.UiObject
+import androidx.test.uiautomator.UiObjectNotFoundException
import androidx.test.uiautomator.UiSelector
import com.google.common.collect.ImmutableList
import com.google.common.truth.Truth.assertThat
@@ -71,6 +73,7 @@
import org.junit.Assume
import org.junit.Assume.assumeFalse
import org.junit.Assume.assumeTrue
+import org.junit.AssumptionViolatedException
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
@@ -112,6 +115,9 @@
@Before
fun setup() {
+ assumeFalse("See b/152082918, Wembley Api30 has a libjpeg issue which causes" +
+ " the test failure.",
+ Build.MODEL.equals("wembley", ignoreCase = true) && Build.VERSION.SDK_INT <= 30)
// Clear the device UI and check if there is no dialog or lock screen on the top of the
// window before start the test.
CoreAppTestUtil.prepareDeviceUI(instrumentation)
@@ -145,7 +151,7 @@
// Act: turn on effect.
val effectToggleId = "androidx.camera.integration.view:id/effect_toggle"
- uiDevice.findObject(UiSelector().resourceId(effectToggleId)).click()
+ assumeObjectCanBeFound(UiSelector().resourceId(effectToggleId)).click()
instrumentation.waitForIdleSync()
// Assert: verify that effect is active.
@@ -161,7 +167,7 @@
// Act: turn on effect.
val effectToggleId = "androidx.camera.integration.view:id/effect_toggle"
- uiDevice.findObject(UiSelector().resourceId(effectToggleId)).click()
+ assumeObjectCanBeFound(UiSelector().resourceId(effectToggleId)).click()
instrumentation.waitForIdleSync()
fragment.assertCanTakePicture()
@@ -203,7 +209,7 @@
// Act: click PreviewView.
val previewViewId = "androidx.camera.integration.view:id/preview_view"
- uiDevice.findObject(UiSelector().resourceId(previewViewId)).click()
+ assumeObjectCanBeFound(UiSelector().resourceId(previewViewId)).click()
// Assert: got a LiveData update
assertThat(focused.tryAcquire(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue()
@@ -789,6 +795,14 @@
)
}
+ private fun assumeObjectCanBeFound(uiSelector: UiSelector): UiObject {
+ return try {
+ uiDevice.findObject(uiSelector)
+ } catch (e: UiObjectNotFoundException) {
+ throw AssumptionViolatedException("Ui object can't be found.")
+ }
+ }
+
/**
* Return value of [CameraControllerFragment.assertCanTakePicture].
*/
diff --git a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IProxyInputConnection.aidl b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IProxyInputConnection.aidl
index a0e60d4..d33caaa 100644
--- a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IProxyInputConnection.aidl
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IProxyInputConnection.aidl
@@ -58,4 +58,5 @@
android.view.inputmethod.ExtractedText getExtractedText(in android.view.inputmethod.ExtractedTextRequest request, int flags) = 22;
void closeConnection() = 23;
android.view.inputmethod.EditorInfo getEditorInfo() = 24;
+ androidx.car.app.serialization.Bundleable getSurroundingText(int beforeLength, int afterLength, int flags) = 25;
}
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/renderer/surface/RemoteProxyInputConnection.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/renderer/surface/RemoteProxyInputConnection.java
index a4a1474..2220696 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/renderer/surface/RemoteProxyInputConnection.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/renderer/surface/RemoteProxyInputConnection.java
@@ -16,21 +16,28 @@
package androidx.car.app.activity.renderer.surface;
+import static androidx.car.app.utils.LogTags.TAG;
+
import static java.util.Objects.requireNonNull;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnectionWrapper;
+import android.view.inputmethod.SurroundingText;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.car.app.activity.ServiceDispatcher;
import androidx.car.app.activity.renderer.IProxyInputConnection;
+import androidx.car.app.serialization.Bundleable;
+import androidx.car.app.serialization.BundlerException;
/** Proxies input connection calls to the provided {@link IProxyInputConnection}. */
final class RemoteProxyInputConnection extends InputConnectionWrapper {
@@ -77,7 +84,7 @@
public ExtractedText getExtractedText(@NonNull ExtractedTextRequest request, int flags) {
requireNonNull(request);
return mServiceDispatcher.fetch("getExtractedText", null, () ->
- mProxyInputConnection.getExtractedText(request, flags));
+ mProxyInputConnection.getExtractedText(request, flags));
}
@Override
@@ -213,6 +220,24 @@
@Nullable
@Override
+ public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ return mServiceDispatcher.fetch("getSurroundingText", null, () -> {
+ try {
+ Bundleable bundleable = mProxyInputConnection.getSurroundingText(beforeLength,
+ afterLength, flags);
+ return bundleable == null ? null : (SurroundingText) bundleable.get();
+ } catch (BundlerException e) {
+ Log.e(TAG, "Cannot get surrounding text", e);
+ return null;
+ }
+ });
+ }
+ return null;
+ }
+
+ @Nullable
+ @Override
public Handler getHandler() {
return null;
}
diff --git a/car/app/app-automotive/src/main/res/values-nl/strings.xml b/car/app/app-automotive/src/main/res/values-nl/strings.xml
index a622f67..d1c0cbd 100644
--- a/car/app/app-automotive/src/main/res/values-nl/strings.xml
+++ b/car/app/app-automotive/src/main/res/values-nl/strings.xml
@@ -18,7 +18,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="error_action_finish" msgid="7621130025103996211">"App sluiten"</string>
- <string name="error_action_update_host" msgid="4802951804749609593">"Checken op updates"</string>
+ <string name="error_action_update_host" msgid="4802951804749609593">"Controleren op updates"</string>
<string name="error_action_retry" msgid="985347670495166517">"Opnieuw proberen"</string>
<string name="error_message_client_side_error" msgid="3323186720368387787">"App-fout. Meld deze fout aan de app-ontwikkelaar."</string>
<string name="error_message_host_error" msgid="5484419926049675696">"Systeemfout"</string>
diff --git a/car/app/app-automotive/src/main/stableAidl/androidx/car/app/activity/renderer/IProxyInputConnection.aidl b/car/app/app-automotive/src/main/stableAidl/androidx/car/app/activity/renderer/IProxyInputConnection.aidl
index 9bd5d7c..430cca5 100644
--- a/car/app/app-automotive/src/main/stableAidl/androidx/car/app/activity/renderer/IProxyInputConnection.aidl
+++ b/car/app/app-automotive/src/main/stableAidl/androidx/car/app/activity/renderer/IProxyInputConnection.aidl
@@ -23,6 +23,7 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
+import androidx.car.app.serialization.Bundleable;
/**
* Proxies the {@link InputConnection} method invocations from {@link CarAppActivity} across a
@@ -102,4 +103,12 @@
/** Returns the {@link EditorInfo} associated with the input connection. */
EditorInfo getEditorInfo() = 24;
-}
\ No newline at end of file
+
+ /**
+ * Proxies a call to {@link InputConnection#getSurroundingText}.
+ * Note that this returns a {@link Bundleable} that wraps a {@link SurroundingText} since the
+ * latter is only available on Android S+. Note that this returns {@code null} on Android R- or
+ * when an exception is thrown.
+ */
+ Bundleable getSurroundingText(int beforeLength, int afterLength, int flags) = 25;
+}
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/renderer/surface/RemoteProxyInputConnectionTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/renderer/surface/RemoteProxyInputConnectionTest.java
new file mode 100644
index 0000000..08d969d
--- /dev/null
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/renderer/surface/RemoteProxyInputConnectionTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.car.app.activity.renderer.surface;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.os.RemoteException;
+import android.view.inputmethod.SurroundingText;
+
+import androidx.car.app.activity.CarAppViewModel;
+import androidx.car.app.activity.ServiceDispatcher;
+import androidx.car.app.activity.renderer.IProxyInputConnection;
+import androidx.car.app.serialization.Bundleable;
+import androidx.car.app.serialization.BundlerException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+/** Tests for {@link RemoteProxyInputConnection} */
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public class RemoteProxyInputConnectionTest {
+ private RemoteProxyInputConnection mRemoteProxyInputConnection;
+ private final CarAppViewModel mViewModel = mock(CarAppViewModel.class);
+ private final ServiceDispatcher mServiceDispatcher = new ServiceDispatcher(mViewModel,
+ () -> true);
+ private final IProxyInputConnection mProxyInputConnection =
+ mock(IProxyInputConnection.class);
+
+
+ @Before
+ public void setUp() throws Exception {
+ mRemoteProxyInputConnection =
+ new RemoteProxyInputConnection(mServiceDispatcher, mProxyInputConnection);
+
+ }
+
+ @Config(maxSdk = 30)
+ @Test
+ public void getSurroundingText_apiLevel30Minus_returnsNull() {
+ assertThat(mRemoteProxyInputConnection.getSurroundingText(10, 10, 0)).isNull();
+ }
+
+ @Config(minSdk = 31)
+ @Test
+ public void getSurroundingText_proxyInputReturnsValidValue_returnsValidValue()
+ throws RemoteException,
+ BundlerException {
+ SurroundingText surroundingText = new SurroundingText("Test Text", 0, 0, 0);
+ when(mProxyInputConnection.getSurroundingText(10, 10, 0)).thenReturn(
+ Bundleable.create(surroundingText));
+ assertThat(mRemoteProxyInputConnection.getSurroundingText(10, 10, 0)).isEqualTo(
+ surroundingText);
+ }
+
+ @Config(minSdk = 31)
+ @Test
+ public void getSurroundingText_proxyInputReturnsNull_returnsNull() throws RemoteException,
+ BundlerException {
+ SurroundingText surroundingText = new SurroundingText("Test Text", 0, 0, 0);
+ when(mProxyInputConnection.getSurroundingText(10, 10, 0)).thenReturn(null);
+ assertThat(mRemoteProxyInputConnection.getSurroundingText(10, 10, 0)).isNull();
+ }
+
+ @Config(minSdk = 31)
+ @Test
+ public void getSurroundingText_throwsRemoteException_returnsNull() throws RemoteException,
+ BundlerException {
+ SurroundingText surroundingText = new SurroundingText("Test Text", 0, 0, 0);
+ when(mProxyInputConnection.getSurroundingText(10, 10, 0)).thenThrow(RemoteException.class);
+ assertThat(mRemoteProxyInputConnection.getSurroundingText(10, 10, 0)).isNull();
+ }
+
+ @Config(minSdk = 31)
+ @Test
+ public void getSurroundingText_throwsBundlerException_returnsNull() throws RemoteException,
+ BundlerException {
+ SurroundingText surroundingText = new SurroundingText("Test Text", 0, 0, 0);
+ when(mProxyInputConnection.getSurroundingText(10, 10, 0)).thenReturn(
+ Bundleable.create("random string"));
+ assertThat(mRemoteProxyInputConnection.getSurroundingText(10, 10, 0)).isNull();
+ }
+}
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml
index 81e17cb..f5d2c1f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml
@@ -243,7 +243,7 @@
<string name="sign_in_template_not_supported_text" msgid="7184733753948837646">"Host ne podržava šablon za prijavljivanje"</string>
<string name="sign_in_template_not_supported_title" msgid="4892883228898541764">"Nekompatibilan host"</string>
<string name="email_hint" msgid="7205549445477319606">"Imejl"</string>
- <string name="sign_in_title" msgid="4551967308262681703">"Prijavite se"</string>
+ <string name="sign_in_title" msgid="4551967308262681703">"Prijavi me"</string>
<string name="sign_in_instructions" msgid="9044850228284294762">"Unesite akreditive"</string>
<string name="invalid_email_error_msg" msgid="5261362663718987167">"Korisničko ime mora da bude važeća imejl adresa"</string>
<string name="invalid_length_error_msg" msgid="8238905276326976425">"Najmanji broj znakova u korisničkom imenu je %s"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-mk/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-mk/strings.xml
index 97ec824..c37b862 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-mk/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-mk/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">"Типови конектори за EV"</string>
<string name="example_title" msgid="530257630320010494">"Пример %d"</string>
<string name="example_1_text" msgid="8456567953748293512">"Текстов има црвена боја"</string>
<string name="example_2_text" msgid="718820705318661440">"Текстов има зелена боја"</string>
@@ -181,7 +181,7 @@
<string name="no_energy_level_permission" msgid="1684773185095107825">"Нема дозвола за EnergyLevel."</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">"Нема дозвола за статусот на EV."</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">"Се презема статус на EV."</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">"Брзина на RAW"</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">"Поврзана е порта за полнење EV"</string>
+ <string name="ev_open" msgid="4916704450914519643">"Отворена е порта за полнење EV"</string>
<string name="accelerometer" msgid="2084026313768299185">"Акцелерометар"</string>
<string name="gyroscope" msgid="3428075828014504651">"Жироскоп"</string>
<string name="compass" msgid="7037367764762441245">"Компас"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml
index e83b300..b52261b 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml
@@ -119,7 +119,7 @@
<string name="loading_demo_row_title" msgid="8933049915126088142">"Ngarkimi përfundoi!"</string>
<string name="pop_to_root" msgid="2078277386355064198">"Hidhu te rrënja"</string>
<string name="pop_to_marker" msgid="5007078308762725207">"Hidhu te shënuesi i demonstrimeve të ndryshme"</string>
- <string name="push_stack" msgid="2433062141810168976">"Shty më tej te stiva"</string>
+ <string name="push_stack" msgid="2433062141810168976">"Shtyj më tej te stiva"</string>
<string name="pop_to_prefix" msgid="4288884615669751608">"Hidhu te"</string>
<string name="pop_to_title" msgid="3924696281273379455">"Hidhu te demonstrimi"</string>
<string name="package_not_found_error_msg" msgid="7525619456883627939">"Paketa nuk u gjet."</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml
index 7b4b837..eb4dc26 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml
@@ -243,7 +243,7 @@
<string name="sign_in_template_not_supported_text" msgid="7184733753948837646">"Хост не подржава шаблон за пријављивање"</string>
<string name="sign_in_template_not_supported_title" msgid="4892883228898541764">"Некомпатибилан хост"</string>
<string name="email_hint" msgid="7205549445477319606">"Имејл"</string>
- <string name="sign_in_title" msgid="4551967308262681703">"Пријавите се"</string>
+ <string name="sign_in_title" msgid="4551967308262681703">"Пријави ме"</string>
<string name="sign_in_instructions" msgid="9044850228284294762">"Унесите акредитиве"</string>
<string name="invalid_email_error_msg" msgid="5261362663718987167">"Корисничко име мора да буде важећа имејл адреса"</string>
<string name="invalid_length_error_msg" msgid="8238905276326976425">"Најмањи број знакова у корисничком имену је %s"</string>
diff --git a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt
index f21dbb9..c38b355 100644
--- a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt
+++ b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt
@@ -219,6 +219,7 @@
) { atEnd ->
if (atEnd) overallDuration.toFloat() else 0f
}
+ @Suppress("UnrememberedMutableState") // b/279909531
return derivedStateOf { interpolate(timeState.value) }
}
diff --git a/compose/compiler/OWNERS b/compose/compiler/OWNERS
index fc11d9b..e48b97d 100644
--- a/compose/compiler/OWNERS
+++ b/compose/compiler/OWNERS
@@ -2,4 +2,5 @@
jsproch@google.com
chuckj@google.com
lelandr@google.com
+anbailey@google.com
per-file settings.gradle = dustinlam@google.com, rahulrav@google.com
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt
index 9731b2f..5b3aa04 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt
@@ -1059,7 +1059,7 @@
}
BasicText(AnnotatedString(
text = "Some text"
- ), null, null, null, <unsafe-coerce>(0), false, 0, 0, null, %composer, 0b0110, 0b000111111110)
+ ), null, null, null, <unsafe-coerce>(0), false, 0, 0, null, null, %composer, 0b0110, 0b001111111110)
if (isTraceInProgress()) {
traceEventEnd()
}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
index 667a9c9..8da7e7c 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
@@ -118,6 +118,7 @@
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.parentClassOrNull
import org.jetbrains.kotlin.ir.util.primaryConstructor
+import org.jetbrains.kotlin.ir.util.properties
import org.jetbrains.kotlin.ir.util.statements
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.load.kotlin.computeJvmDescriptor
@@ -264,8 +265,15 @@
).unboxValueIfInline()
} else {
val primaryValueParameter = klass.primaryConstructor?.valueParameters?.get(0)
- ?: error("Expected a value parameter")
- val fieldGetter = klass.getPropertyGetter(primaryValueParameter.name.identifier)
+ val cantUnbox = primaryValueParameter == null || klass.properties.none {
+ it.name == primaryValueParameter.name && it.getter != null
+ }
+ if (cantUnbox) {
+ // LazyIr (external module) doesn't show a getter of a private property.
+ // So we can't unbox the value
+ return this
+ }
+ val fieldGetter = klass.getPropertyGetter(primaryValueParameter!!.name.identifier)
?: error("Expected a getter")
return irCall(
symbol = fieldGetter,
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
index 75033ec..43cb8fe 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
@@ -2137,8 +2137,8 @@
// boxed, then we don't want to unnecessarily _unbox_ it. Note that if Kotlin allows for
// an overridden equals method of inline classes in the future, we may have to avoid the
// boxing in a different way.
- val type = value.type.unboxInlineClass()
val expr = value.unboxValueIfInline()
+ val type = expr.type
val descriptor = type
.toPrimitiveType()
.let { changedPrimitiveFunctions[it] }
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt
index 016b6e8..83a8184 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt
@@ -22,23 +22,16 @@
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
import org.jetbrains.kotlin.backend.common.peek
import org.jetbrains.kotlin.backend.common.pop
-import org.jetbrains.kotlin.ir.IrElement
-import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
-import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
-import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
-import org.jetbrains.kotlin.ir.declarations.IrLocalDelegatedProperty
-import org.jetbrains.kotlin.ir.declarations.IrMetadataSourceOwner
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrTypeParametersContainer
import org.jetbrains.kotlin.ir.declarations.copyAttributes
-import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
@@ -58,7 +51,6 @@
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.types.isClassWithFqName
import org.jetbrains.kotlin.ir.types.typeOrNull
-import org.jetbrains.kotlin.ir.util.DeepCopyIrTreeWithSymbols
import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
import org.jetbrains.kotlin.ir.util.SymbolRemapper
import org.jetbrains.kotlin.ir.util.SymbolRenamer
@@ -74,28 +66,12 @@
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.types.Variance
-class DeepCopyIrTreeWithSymbolsPreservingMetadata(
+internal class DeepCopyIrTreeWithRemappedComposableTypes(
private val context: IrPluginContext,
private val symbolRemapper: DeepCopySymbolRemapper,
typeRemapper: TypeRemapper,
symbolRenamer: SymbolRenamer = SymbolRenamer.DEFAULT
-) : DeepCopyIrTreeWithSymbols(symbolRemapper, typeRemapper, symbolRenamer) {
-
- override fun visitClass(declaration: IrClass): IrClass {
- return super.visitClass(declaration).also { it.copyMetadataFrom(declaration) }
- }
-
- override fun visitFunction(declaration: IrFunction): IrStatement {
- return super.visitFunction(declaration).also {
- it.copyMetadataFrom(declaration)
- }
- }
-
- override fun visitConstructor(declaration: IrConstructor): IrConstructor {
- return super.visitConstructor(declaration).also {
- it.copyMetadataFrom(declaration)
- }
- }
+) : DeepCopyPreservingMetadata(symbolRemapper, typeRemapper, symbolRenamer) {
override fun visitSimpleFunction(declaration: IrSimpleFunction): IrSimpleFunction {
if (declaration.symbol.isRemappedAndBound()) {
@@ -106,30 +82,17 @@
}
return super.visitSimpleFunction(declaration).also {
it.correspondingPropertySymbol = declaration.correspondingPropertySymbol
- it.copyMetadataFrom(declaration)
}
}
-
- override fun visitField(declaration: IrField): IrField {
- return super.visitField(declaration).also {
- it.metadata = declaration.metadata
- }
- }
-
override fun visitProperty(declaration: IrProperty): IrProperty {
return super.visitProperty(declaration).also {
- it.copyMetadataFrom(declaration)
it.copyAttributes(declaration)
}
}
override fun visitFile(declaration: IrFile): IrFile {
includeFileNameInExceptionTrace(declaration) {
- return super.visitFile(declaration).also {
- if (it is IrFileImpl) {
- it.metadata = declaration.metadata
- }
- }
+ return super.visitFile(declaration)
}
}
@@ -188,13 +151,6 @@
return super.visitConstructorCall(expression)
}
- override fun visitLocalDelegatedProperty(
- declaration: IrLocalDelegatedProperty
- ): IrLocalDelegatedProperty =
- super.visitLocalDelegatedProperty(declaration).apply {
- metadata = declaration.metadata
- }
-
private fun IrFunction.needsComposableRemapping(): Boolean {
if (
needsComposableRemapping(dispatchReceiverParameter?.type) ||
@@ -390,14 +346,6 @@
extensionReceiver = original.extensionReceiver?.transform()
}
- private fun IrElement.copyMetadataFrom(owner: IrMetadataSourceOwner) {
- if (this is IrMetadataSourceOwner) {
- metadata = owner.metadata
- } else {
- throw IllegalArgumentException("Cannot copy metadata to $this")
- }
- }
-
private fun IrType.isComposable(): Boolean {
return annotations.hasAnnotation(ComposeFqNames.Composable)
}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
index 0980e11b..7bb80ae 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
@@ -120,7 +120,7 @@
// for each declaration, we create a deepCopy transformer It is important here that we
// use the "preserving metadata" variant since we are using this copy to *replace* the
// originals, or else the module we would produce wouldn't have any metadata in it.
- val transformer = DeepCopyIrTreeWithSymbolsPreservingMetadata(
+ val transformer = DeepCopyIrTreeWithRemappedComposableTypes(
context,
symbolRemapper,
typeRemapper
@@ -629,9 +629,18 @@
private fun defaultParameterType(param: IrValueParameter): IrType {
val type = param.type
if (param.defaultValue == null) return type
+ val constructorAccessible = !type.isPrimitiveType() &&
+ type.classOrNull?.owner?.primaryConstructor != null
return when {
type.isPrimitiveType() -> type
- type.isInlineClassType() -> type
+ type.isInlineClassType() -> if (context.platform.isJvm() || constructorAccessible) {
+ type
+ } else {
+ // k/js and k/native: private constructors of value classes can be not accessible.
+ // Therefore it won't be possible to create a "fake" default argument for calls.
+ // Making it nullable allows to pass null.
+ type.makeNullable()
+ }
else -> type.makeNullable()
}
}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/DeepCopyPreservingMetadata.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/DeepCopyPreservingMetadata.kt
new file mode 100644
index 0000000..3ef6491
--- /dev/null
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/DeepCopyPreservingMetadata.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.compose.compiler.plugins.kotlin.lower
+
+import org.jetbrains.kotlin.ir.declarations.IrClass
+import org.jetbrains.kotlin.ir.declarations.IrConstructor
+import org.jetbrains.kotlin.ir.declarations.IrField
+import org.jetbrains.kotlin.ir.declarations.IrFile
+import org.jetbrains.kotlin.ir.declarations.IrLocalDelegatedProperty
+import org.jetbrains.kotlin.ir.declarations.IrProperty
+import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
+import org.jetbrains.kotlin.ir.util.DeepCopyIrTreeWithSymbols
+import org.jetbrains.kotlin.ir.util.SymbolRemapper
+import org.jetbrains.kotlin.ir.util.SymbolRenamer
+import org.jetbrains.kotlin.ir.util.TypeRemapper
+
+internal open class DeepCopyPreservingMetadata(
+ symbolRemapper: SymbolRemapper,
+ typeRemapper: TypeRemapper,
+ symbolRenamer: SymbolRenamer
+) : DeepCopyIrTreeWithSymbols(symbolRemapper, typeRemapper, symbolRenamer) {
+ override fun visitFile(declaration: IrFile): IrFile =
+ super.visitFile(declaration).apply {
+ metadata = declaration.metadata
+ }
+
+ override fun visitClass(declaration: IrClass): IrClass =
+ super.visitClass(declaration).apply {
+ metadata = declaration.metadata
+ }
+
+ override fun visitConstructor(declaration: IrConstructor): IrConstructor =
+ super.visitConstructor(declaration).apply {
+ metadata = declaration.metadata
+ }
+
+ override fun visitSimpleFunction(declaration: IrSimpleFunction): IrSimpleFunction =
+ super.visitSimpleFunction(declaration).apply {
+ metadata = declaration.metadata
+ }
+
+ override fun visitProperty(declaration: IrProperty): IrProperty =
+ super.visitProperty(declaration).apply {
+ metadata = declaration.metadata
+ }
+
+ override fun visitField(declaration: IrField): IrField =
+ super.visitField(declaration).apply {
+ metadata = declaration.metadata
+ }
+
+ override fun visitLocalDelegatedProperty(
+ declaration: IrLocalDelegatedProperty
+ ): IrLocalDelegatedProperty =
+ super.visitLocalDelegatedProperty(declaration).apply {
+ metadata = declaration.metadata
+ }
+}
\ No newline at end of file
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/DecoyTransformBase.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/DecoyTransformBase.kt
index 6143aff..632e6b00 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/DecoyTransformBase.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/DecoyTransformBase.kt
@@ -16,6 +16,7 @@
package androidx.compose.compiler.plugins.kotlin.lower.decoys
+import androidx.compose.compiler.plugins.kotlin.lower.DeepCopyPreservingMetadata
import androidx.compose.compiler.plugins.kotlin.lower.hasAnnotationSafe
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
@@ -24,16 +25,9 @@
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
-import org.jetbrains.kotlin.ir.declarations.IrClass
-import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
-import org.jetbrains.kotlin.ir.declarations.IrField
-import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
-import org.jetbrains.kotlin.ir.declarations.IrLocalDelegatedProperty
-import org.jetbrains.kotlin.ir.declarations.IrProperty
-import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrVararg
@@ -47,10 +41,8 @@
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeArgument
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
-import org.jetbrains.kotlin.ir.util.DeepCopyIrTreeWithSymbols
import org.jetbrains.kotlin.ir.util.DeepCopyTypeRemapper
import org.jetbrains.kotlin.ir.util.IdSignature
-import org.jetbrains.kotlin.ir.util.SymbolRemapper
import org.jetbrains.kotlin.ir.util.SymbolRenamer
import org.jetbrains.kotlin.ir.util.TypeRemapper
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
@@ -217,7 +209,7 @@
return typeRemapper.remapType(type.remapTypeParameters(source, target))
}
}
- val deepCopy = DeepCopySavingMetadata(
+ val deepCopy = DeepCopyPreservingMetadata(
symbolRemapper,
typeParamRemapper,
SymbolRenamer.DEFAULT
@@ -226,46 +218,3 @@
deepCopy
}
}
-
-internal class DeepCopySavingMetadata(
- symbolRemapper: SymbolRemapper,
- typeRemapper: TypeRemapper,
- symbolRenamer: SymbolRenamer
-) : DeepCopyIrTreeWithSymbols(symbolRemapper, typeRemapper, symbolRenamer) {
- override fun visitFile(declaration: IrFile): IrFile =
- super.visitFile(declaration).apply {
- metadata = declaration.metadata
- }
-
- override fun visitClass(declaration: IrClass): IrClass =
- super.visitClass(declaration).apply {
- metadata = declaration.metadata
- }
-
- override fun visitConstructor(declaration: IrConstructor): IrConstructor =
- super.visitConstructor(declaration).apply {
- metadata = declaration.metadata
- }
-
- override fun visitSimpleFunction(declaration: IrSimpleFunction): IrSimpleFunction =
- super.visitSimpleFunction(declaration).apply {
- metadata = declaration.metadata
- }
-
- override fun visitProperty(declaration: IrProperty): IrProperty =
- super.visitProperty(declaration).apply {
- metadata = declaration.metadata
- }
-
- override fun visitField(declaration: IrField): IrField =
- super.visitField(declaration).apply {
- metadata = declaration.metadata
- }
-
- override fun visitLocalDelegatedProperty(
- declaration: IrLocalDelegatedProperty
- ): IrLocalDelegatedProperty =
- super.visitLocalDelegatedProperty(declaration).apply {
- metadata = declaration.metadata
- }
-}
\ No newline at end of file
diff --git a/compose/desktop/desktop/build.gradle b/compose/desktop/desktop/build.gradle
index a8baf7a..46e9c83 100644
--- a/compose/desktop/desktop/build.gradle
+++ b/compose/desktop/desktop/build.gradle
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+
+import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
import androidx.build.LibraryType
import androidx.build.RunApiTasks
import androidx.build.BuildOnServerKt
@@ -26,41 +29,55 @@
id("kotlin-multiplatform")
}
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
+
dependencies {
}
kotlin {
- jvm() {
- withJava()
+ if (desktopEnabled) {
+ jvm() {
+ withJava()
+ }
}
- sourceSets {
- commonMain.dependencies {
- implementation(libs.kotlinStdlibCommon)
- implementation(project(":compose:ui:ui-util"))
- api(project(":compose:foundation:foundation"))
- api(project(":compose:material:material"))
- api(project(":compose:runtime:runtime"))
- api(project(":compose:ui:ui"))
- api(project(":compose:ui:ui-tooling-preview"))
- }
-
- jvmMain.dependencies {
- implementation(libs.kotlinStdlib)
- implementation(libs.kotlinStdlibJdk8)
- implementation(libs.kotlinCoroutinesCore)
- }
-
- jvmTest {
- resources.srcDirs += new File(SupportConfigKt.getExternalProjectPath(project), "noto-fonts/other/")
- resources.srcDirs += "src/jvmTest/res"
- dependencies {
- implementation(libs.kotlinCoroutinesTest)
- implementation(libs.skikoCurrentOs)
- implementation(project(":compose:ui:ui-test-junit4"))
- implementation(libs.junit)
- implementation(libs.truth)
+ if (desktopEnabled) {
+ sourceSets {
+ commonMain.dependencies {
+ implementation(libs.kotlinStdlibCommon)
+ implementation(project(":compose:ui:ui-util"))
+ api(project(":compose:foundation:foundation"))
+ api(project(":compose:material:material"))
+ api(project(":compose:runtime:runtime"))
+ api(project(":compose:ui:ui"))
+ api(project(":compose:ui:ui-tooling-preview"))
}
+
+ jvmMain.dependencies {
+ implementation(libs.kotlinStdlib)
+ implementation(libs.kotlinStdlibJdk8)
+ implementation(libs.kotlinCoroutinesCore)
+ }
+
+ jvmTest {
+ resources.srcDirs += new File(SupportConfigKt.getExternalProjectPath(project), "noto-fonts/other/")
+ resources.srcDirs += "src/jvmTest/res"
+ dependencies {
+ implementation(libs.kotlinCoroutinesTest)
+ implementation(libs.skikoCurrentOs)
+ implementation(project(":compose:ui:ui-test-junit4"))
+ implementation(libs.junit)
+ implementation(libs.truth)
+ }
+ }
+ }
+ }
+}
+
+if (!desktopEnabled) {
+ kotlin {
+ jvm("disabled") {
+ withJava()
}
}
}
@@ -74,8 +91,10 @@
}
}
-tasks.findByName("jvmTest").configure {
- systemProperties["GOLDEN_PATH"] = getGoldenPath(project).toString()
+if (desktopEnabled) {
+ tasks.findByName("jvmTest").configure {
+ systemProperties["GOLDEN_PATH"] = getGoldenPath(project).toString()
+ }
}
androidx {
@@ -127,4 +146,6 @@
}
}
-BuildOnServerKt.addToBuildOnServer(project, "${project.path}:jvmJar")
+if (desktopEnabled) {
+ BuildOnServerKt.addToBuildOnServer(project, "${project.path}:jvmJar")
+}
diff --git a/compose/desktop/desktop/samples/build.gradle b/compose/desktop/desktop/samples/build.gradle
index 76b09be..7502716 100644
--- a/compose/desktop/desktop/samples/build.gradle
+++ b/compose/desktop/desktop/samples/build.gradle
@@ -15,94 +15,105 @@
*/
import androidx.build.BuildOnServerKt
+import androidx.build.KmpPlatformsKt
import androidx.build.SupportConfigKt
plugins {
id("AndroidXPlugin")
+ id("java")
id("AndroidXComposePlugin")
id("kotlin-multiplatform")
}
-dependencies {
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
+
+if (desktopEnabled) {
+ kotlin {
+ jvm()
+
+ sourceSets {
+ jvmMain {
+ resources.srcDirs += new File(SupportConfigKt.getExternalProjectPath(project), "noto-fonts/other/")
+ resources.srcDirs += "src/jvmMain/res"
+ }
+
+ jvmMain.dependencies {
+ implementation(libs.skikoCurrentOs)
+ implementation(project(":compose:desktop:desktop"))
+ }
+ }
+ }
+
+ task run1(type: JavaExec) {
+ dependsOn(":compose:desktop:desktop:jar")
+ main = "androidx.compose.desktop.examples.example1.Main_jvmKt"
+ systemProperty("skiko.fps.enabled", "true")
+ def compilation = kotlin.jvm().compilations["main"]
+ classpath =
+ compilation.output.allOutputs +
+ compilation.runtimeDependencyFiles
+ }
+
+ task run2(type: JavaExec) {
+ dependsOn(":compose:desktop:desktop:jar")
+ main = "androidx.compose.desktop.examples.example2.Main_jvmKt"
+ def compilation = kotlin.jvm().compilations["main"]
+ classpath =
+ compilation.output.allOutputs +
+ compilation.runtimeDependencyFiles
+ }
+
+ task run3(type: JavaExec) {
+ dependsOn(":compose:desktop:desktop:jar")
+ main = "androidx.compose.desktop.examples.popupexample.Main_jvmKt"
+ def compilation = kotlin.jvm().compilations["main"]
+ classpath =
+ compilation.output.allOutputs +
+ compilation.runtimeDependencyFiles
+ }
+
+ task run4(type: JavaExec) {
+ dependsOn(":compose:desktop:desktop:jar")
+ main = "androidx.compose.desktop.examples.swingexample.Main_jvmKt"
+ def compilation = kotlin.jvm().compilations["main"]
+ classpath =
+ compilation.output.allOutputs +
+ compilation.runtimeDependencyFiles
+ }
+
+ task runVsync(type: JavaExec) {
+ dependsOn(":compose:desktop:desktop:jar")
+ main = "androidx.compose.desktop.examples.vsynctest.Main_jvmKt"
+ jvmArgs("-verbose:gc")
+ def compilation = kotlin.jvm().compilations["main"]
+ classpath =
+ compilation.output.allOutputs +
+ compilation.runtimeDependencyFiles
+ }
+
+ task runWindowApi(type: JavaExec) {
+ dependsOn(":compose:desktop:desktop:jar")
+ main = "androidx.compose.desktop.examples.windowapi.Main_jvmKt"
+ def compilation = kotlin.jvm().compilations["main"]
+ systemProperty("skiko.rendering.laf.global", "true")
+ systemProperty("skiko.rendering.useScreenMenuBar", "true")
+ classpath =
+ compilation.output.allOutputs +
+ compilation.runtimeDependencyFiles
+ }
+
+ task run {
+ dependsOn("run1")
+ }
+
+
+ BuildOnServerKt.addToBuildOnServer(project, "${project.path}:jvmJar")
}
-kotlin {
- jvm()
-
- sourceSets {
- jvmMain {
- resources.srcDirs += new File(SupportConfigKt.getExternalProjectPath(project), "noto-fonts/other/")
- resources.srcDirs += "src/jvmMain/res"
- }
-
- jvmMain.dependencies {
- implementation(libs.skikoCurrentOs)
- implementation(project(":compose:desktop:desktop"))
+if (!desktopEnabled) {
+ kotlin {
+ jvm("disabled") {
+ withJava()
}
}
}
-
-task run1(type: JavaExec) {
- dependsOn(":compose:desktop:desktop:jar")
- main = "androidx.compose.desktop.examples.example1.Main_jvmKt"
- systemProperty("skiko.fps.enabled", "true")
- def compilation = kotlin.jvm().compilations["main"]
- classpath =
- compilation.output.allOutputs +
- compilation.runtimeDependencyFiles
-}
-
-task run2(type: JavaExec) {
- dependsOn(":compose:desktop:desktop:jar")
- main = "androidx.compose.desktop.examples.example2.Main_jvmKt"
- def compilation = kotlin.jvm().compilations["main"]
- classpath =
- compilation.output.allOutputs +
- compilation.runtimeDependencyFiles
-}
-
-task run3(type: JavaExec) {
- dependsOn(":compose:desktop:desktop:jar")
- main = "androidx.compose.desktop.examples.popupexample.Main_jvmKt"
- def compilation = kotlin.jvm().compilations["main"]
- classpath =
- compilation.output.allOutputs +
- compilation.runtimeDependencyFiles
-}
-
-task run4(type: JavaExec) {
- dependsOn(":compose:desktop:desktop:jar")
- main = "androidx.compose.desktop.examples.swingexample.Main_jvmKt"
- def compilation = kotlin.jvm().compilations["main"]
- classpath =
- compilation.output.allOutputs +
- compilation.runtimeDependencyFiles
-}
-
-task runVsync(type: JavaExec) {
- dependsOn(":compose:desktop:desktop:jar")
- main = "androidx.compose.desktop.examples.vsynctest.Main_jvmKt"
- jvmArgs("-verbose:gc")
- def compilation = kotlin.jvm().compilations["main"]
- classpath =
- compilation.output.allOutputs +
- compilation.runtimeDependencyFiles
-}
-
-task runWindowApi(type: JavaExec) {
- dependsOn(":compose:desktop:desktop:jar")
- main = "androidx.compose.desktop.examples.windowapi.Main_jvmKt"
- def compilation = kotlin.jvm().compilations["main"]
- systemProperty("skiko.rendering.laf.global", "true")
- systemProperty("skiko.rendering.useScreenMenuBar", "true")
- classpath =
- compilation.output.allOutputs +
- compilation.runtimeDependencyFiles
-}
-
-task run {
- dependsOn("run1")
-}
-
-
-BuildOnServerKt.addToBuildOnServer(project, "${project.path}:jvmJar")
diff --git a/compose/desktop/desktop/samples/src/jvmMain/disabled/Empty.kt b/compose/desktop/desktop/samples/src/jvmMain/disabled/Empty.kt
new file mode 100644
index 0000000..0c32401
--- /dev/null
+++ b/compose/desktop/desktop/samples/src/jvmMain/disabled/Empty.kt
@@ -0,0 +1 @@
+// Give Kotlin a placeholder file to compile when this project is disabled.
\ No newline at end of file
diff --git a/compose/desktop/desktop/src/disabled/Empty.kt b/compose/desktop/desktop/src/disabled/Empty.kt
new file mode 100644
index 0000000..0c32401
--- /dev/null
+++ b/compose/desktop/desktop/src/disabled/Empty.kt
@@ -0,0 +1 @@
+// Give Kotlin a placeholder file to compile when this project is disabled.
\ No newline at end of file
diff --git a/compose/foundation/foundation-layout/api/current.txt b/compose/foundation/foundation-layout/api/current.txt
index 4339957..c8e0c04 100644
--- a/compose/foundation/foundation-layout/api/current.txt
+++ b/compose/foundation/foundation-layout/api/current.txt
@@ -111,12 +111,6 @@
method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, float weight, optional boolean fill);
}
- @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable @kotlin.jvm.JvmDefaultWithCompatibility public interface FlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
- }
-
- @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable @kotlin.jvm.JvmDefaultWithCompatibility public interface FlowRowScope extends androidx.compose.foundation.layout.RowScope {
- }
-
public final class IntrinsicKt {
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredHeight(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
diff --git a/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt b/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt
index 3e7c323..3d72dbe 100644
--- a/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt
@@ -114,7 +114,7 @@
@kotlin.RequiresOptIn(message="The API of this layout is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalLayoutApi {
}
- @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable @kotlin.jvm.JvmDefaultWithCompatibility public interface FlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
+ @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
}
public final class FlowLayoutKt {
@@ -122,7 +122,7 @@
method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static inline void FlowRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional int maxItemsInEachRow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowScope,kotlin.Unit> content);
}
- @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable @kotlin.jvm.JvmDefaultWithCompatibility public interface FlowRowScope extends androidx.compose.foundation.layout.RowScope {
+ @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowRowScope extends androidx.compose.foundation.layout.RowScope {
}
public final class IntrinsicKt {
diff --git a/compose/foundation/foundation-layout/api/restricted_current.txt b/compose/foundation/foundation-layout/api/restricted_current.txt
index 2b75f19..03cdfb3 100644
--- a/compose/foundation/foundation-layout/api/restricted_current.txt
+++ b/compose/foundation/foundation-layout/api/restricted_current.txt
@@ -114,17 +114,11 @@
method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, float weight, optional boolean fill);
}
- @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable @kotlin.jvm.JvmDefaultWithCompatibility public interface FlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
- }
-
public final class FlowLayoutKt {
method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static androidx.compose.ui.layout.MeasurePolicy columnMeasurementHelper(androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, int maxItemsInMainAxis);
method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static androidx.compose.ui.layout.MeasurePolicy rowMeasurementHelper(androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, int maxItemsInMainAxis);
}
- @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable @kotlin.jvm.JvmDefaultWithCompatibility public interface FlowRowScope extends androidx.compose.foundation.layout.RowScope {
- }
-
public final class IntrinsicKt {
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredHeight(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt
index 5d58064..cdec1d5 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt
@@ -126,7 +126,7 @@
*/
@LayoutScopeMarker
@Immutable
-@JvmDefaultWithCompatibility
+@ExperimentalLayoutApi
interface FlowRowScope : RowScope
/**
@@ -134,11 +134,13 @@
*/
@LayoutScopeMarker
@Immutable
-@JvmDefaultWithCompatibility
+@ExperimentalLayoutApi
interface FlowColumnScope : ColumnScope
+@OptIn(ExperimentalLayoutApi::class)
internal object FlowRowScopeInstance : RowScope by RowScopeInstance, FlowRowScope
+@OptIn(ExperimentalLayoutApi::class)
internal object FlowColumnScopeInstance : ColumnScope by ColumnScopeInstance, FlowColumnScope
private fun getVerticalArrangement(verticalArrangement: Arrangement.Vertical):
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt
index 78210e2..b0a80c7 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt
@@ -38,7 +38,6 @@
import androidx.compose.ui.unit.constrain
import androidx.compose.ui.unit.constrainHeight
import androidx.compose.ui.unit.constrainWidth
-import androidx.compose.ui.unit.dp
import kotlin.math.roundToInt
/**
@@ -768,12 +767,12 @@
private val Density.targetConstraints: Constraints
get() {
val maxWidth = if (maxWidth != Dp.Unspecified) {
- maxWidth.coerceAtLeast(0.dp).roundToPx()
+ maxWidth.roundToPx().coerceAtLeast(0)
} else {
Constraints.Infinity
}
val maxHeight = if (maxHeight != Dp.Unspecified) {
- maxHeight.coerceAtLeast(0.dp).roundToPx()
+ maxHeight.roundToPx().coerceAtLeast(0)
} else {
Constraints.Infinity
}
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index db960fa..32cc4078 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -866,10 +866,12 @@
}
public final class BasicTextKt {
- method @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines);
- method @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent);
+ method @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional androidx.compose.ui.graphics.ColorProducer? color);
+ method @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional androidx.compose.ui.graphics.ColorProducer? color);
method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines);
method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent);
+ method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines);
+ method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent);
}
public final class ClickableTextKt {
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index 901c378..c37d48d 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -1214,10 +1214,12 @@
}
public final class BasicTextKt {
- method @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines);
- method @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent);
+ method @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional androidx.compose.ui.graphics.ColorProducer? color);
+ method @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional androidx.compose.ui.graphics.ColorProducer? color);
method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines);
method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent);
+ method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines);
+ method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent);
}
public final class ClickableTextKt {
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index db960fa..32cc4078 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -866,10 +866,12 @@
}
public final class BasicTextKt {
- method @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines);
- method @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent);
+ method @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional androidx.compose.ui.graphics.ColorProducer? color);
+ method @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional androidx.compose.ui.graphics.ColorProducer? color);
method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines);
method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent);
+ method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines);
+ method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent);
}
public final class ClickableTextKt {
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/lazyhosted/TextLazyReuse.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/lazyhosted/TextLazyReuse.kt
index e0f379e..f6b097b 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/lazyhosted/TextLazyReuse.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/lazyhosted/TextLazyReuse.kt
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalComposeUiApi::class)
-
package androidx.compose.foundation.benchmark.text.lazyhosted
import androidx.compose.foundation.layout.fillMaxWidth
@@ -30,9 +28,7 @@
import androidx.compose.testutils.benchmark.toggleStateBenchmarkComposeMeasureLayout
import androidx.compose.testutils.benchmark.toggleStateBenchmarkDraw
import androidx.compose.testutils.benchmark.toggleStateBenchmarkRecompose
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -50,8 +46,7 @@
private var reuseKey = mutableStateOf(0)
private val style = TextStyle.Default.copy(
- fontFamily = FontFamily.Monospace,
- color = Color.Red,
+ fontFamily = FontFamily.Monospace
)
@Composable
@@ -61,7 +56,6 @@
Text(
toggleText.value,
style = style,
- color = style.color, /* for now, ignore color merge allocs */
modifier = Modifier.fillMaxWidth()
)
}
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/BasicMarqueeDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/BasicMarqueeDemo.kt
index 3155a96..1655ea6 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/BasicMarqueeDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/BasicMarqueeDemo.kt
@@ -28,6 +28,7 @@
import androidx.compose.foundation.MarqueeAnimationMode
import androidx.compose.foundation.MarqueeAnimationMode.Companion.Immediately
import androidx.compose.foundation.MarqueeAnimationMode.Companion.WhileFocused
+import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.focusable
@@ -40,7 +41,6 @@
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.samples.BasicFocusableMarqueeSample
import androidx.compose.foundation.samples.BasicMarqueeSample
import androidx.compose.foundation.samples.BasicMarqueeWithFadedEdgesSample
@@ -91,6 +91,7 @@
Spacer(Modifier.height(16.dp))
Divider()
Text("Compose marquees:", style = MaterialTheme.typography.subtitle1)
+
MarqueeText("short", Modifier.fillMaxWidth())
listOf(40.dp, 80.dp, 120.dp).forEach {
MarqueeText("long text in short marquee", Modifier.width(it))
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/MemoryAllocs.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/MemoryAllocs.kt
index 1603fdc..5e2b764 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/MemoryAllocs.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/MemoryAllocs.kt
@@ -35,7 +35,7 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-private val style = TextStyle(color = Color.Magenta)
+private val style = TextStyle.Default
/**
* These demos are for using the memory profiler to observe initial compo and recompo memory
* pressure.
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextAnimationDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextAnimationDemo.kt
index 018fda0..1f7432f 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextAnimationDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextAnimationDemo.kt
@@ -16,6 +16,9 @@
package androidx.compose.foundation.demos.text
+import androidx.compose.animation.animateColor
+import androidx.compose.animation.core.CubicBezierEasing
+import androidx.compose.animation.core.InfiniteRepeatableSpec
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
@@ -23,14 +26,18 @@
import androidx.compose.animation.core.tween
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.text.BasicText
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.RadioButton
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -38,10 +45,16 @@
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextMotion
+import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -62,6 +75,39 @@
}
}
}
+@Composable
+fun TextColorAnimation() {
+ val anim = rememberInfiniteTransition("slow animation")
+ val color: State<Color> = anim.animateColor(
+ initialValue = Color.Black,
+ targetValue = Color.Gray,
+ animationSpec = InfiniteRepeatableSpec(
+ tween(5_000, 50, CubicBezierEasing(0.2f, 0.0f, 0.5f, 0.6f)),
+ repeatMode = RepeatMode.Reverse
+ ),
+ label = "slow gray"
+ )
+ Box(contentAlignment = Alignment.Center) {
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ BasicText(
+ "This text has animated color",
+ style = TextStyle.Default.copy(fontSize = 45.sp, textAlign = TextAlign.Center),
+ color = { color.value }
+ )
+ BasicText(
+ buildAnnotatedString {
+ append("So does ")
+ withStyle(SpanStyle(fontWeight = FontWeight.Black)) {
+ append("this")
+ }
+ },
+ style = TextStyle.Default.copy(fontSize = 30.sp, textAlign = TextAlign.Center),
+ color = { color.value },
+ modifier = Modifier.padding(top = 16.dp)
+ )
+ }
+ }
+}
class TextMotionState(initialTextStyle: TextStyle) {
var isStatic by mutableStateOf(true)
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt
index 29d7a42..d48487a 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt
@@ -32,6 +32,13 @@
)
),
DemoCategory(
+ "Animation",
+ listOf(
+ ComposableDemo("color = { animatedColor.value }") { TextColorAnimation() },
+ ComposableDemo("GraphicsLayer (skew, scale, etc)") { TextAnimationDemo() },
+ )
+ ),
+ DemoCategory(
"Text Layout",
listOf(
ComposableDemo("Static text") { TextDemo() },
@@ -67,7 +74,6 @@
ComposableDemo("Layout Reuse") { TextReuseLayoutDemo() },
ComposableDemo("Multi paragraph") { MultiParagraphDemo() },
ComposableDemo("Interactive text") { InteractiveTextDemo() },
- ComposableDemo("Text Animation") { TextAnimationDemo() },
)
),
DemoCategory(
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt
index b95aeb5..78597e0 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt
@@ -21,25 +21,39 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PageSize
import androidx.compose.foundation.pager.VerticalPager
import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.Text
+import androidx.compose.material.TopAppBar
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import kotlin.math.roundToInt
import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class)
@@ -254,4 +268,73 @@
}
}
}
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Sampled
+@Composable
+fun HorizontalPagerWithScrollableContent() {
+ // This is a sample using NestedScroll and Pager.
+ // We use the toolbar offset changing example from
+ // androidx.compose.ui.samples.NestedScrollConnectionSample
+
+ val pagerState = rememberPagerState { 10 }
+
+ val toolbarHeight = 48.dp
+ val toolbarHeightPx = with(LocalDensity.current) { toolbarHeight.roundToPx().toFloat() }
+ val toolbarOffsetHeightPx = remember { mutableStateOf(0f) }
+ val nestedScrollConnection = remember {
+ object : NestedScrollConnection {
+ override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ val delta = available.y
+ val newOffset = toolbarOffsetHeightPx.value + delta
+ toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
+ return Offset.Zero
+ }
+ }
+ }
+
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .nestedScroll(nestedScrollConnection)
+ ) {
+ TopAppBar(
+ modifier = Modifier
+ .height(toolbarHeight)
+ .offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) },
+ title = { Text("Toolbar offset is ${toolbarOffsetHeightPx.value}") }
+ )
+
+ val paddingOffset =
+ toolbarHeight + with(LocalDensity.current) { toolbarOffsetHeightPx.value.toDp() }
+
+ HorizontalPager(
+ modifier = Modifier.fillMaxSize(),
+ state = pagerState,
+ contentPadding = PaddingValues(top = paddingOffset)
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .verticalScroll(rememberScrollState())
+ ) {
+ repeat(20) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(64.dp)
+ .padding(4.dp)
+ .background(if (it % 2 == 0) Color.Black else Color.Yellow),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = it.toString(),
+ color = if (it % 2 != 0) Color.Black else Color.Yellow
+ )
+ }
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BasicMarqueeTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BasicMarqueeTest.kt
index 37cd6eb..ff170d3 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BasicMarqueeTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BasicMarqueeTest.kt
@@ -23,6 +23,7 @@
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
@@ -40,15 +41,25 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.toPixelMap
+import androidx.compose.ui.layout.IntrinsicMeasurable
+import androidx.compose.ui.layout.IntrinsicMeasureScope
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasurePolicy
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.captureToImage
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.filters.SdkSuppress
@@ -125,7 +136,8 @@
@Suppress("UnnecessaryOptInAnnotation")
@OptIn(ExperimentalTestApi::class)
- @Test fun animates_whenAnimationsDisabledBySystem() {
+ @Test
+ fun animates_whenAnimationsDisabledBySystem() {
motionDurationScale.scaleFactor = 0f
rule.setContent {
@@ -1041,6 +1053,112 @@
}
}
+ @Test
+ fun intrinsicsCalculations() {
+ val childMinIntrinsicWidth = 10
+ val childMaxIntrinsicWidth = 20
+ val childMinIntrinsicHeight = 30
+ val childMaxIntrinsicHeight = 40
+ val fixedIntrinsicsMeasurePolicy = object : MeasurePolicy {
+ override fun MeasureScope.measure(
+ measurables: List<Measurable>,
+ constraints: Constraints
+ ): MeasureResult = layout(0, 0) {}
+
+ override fun IntrinsicMeasureScope.minIntrinsicWidth(
+ measurables: List<IntrinsicMeasurable>,
+ height: Int
+ ): Int = childMinIntrinsicWidth
+
+ override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+ measurables: List<IntrinsicMeasurable>,
+ height: Int
+ ): Int = childMaxIntrinsicWidth
+
+ override fun IntrinsicMeasureScope.minIntrinsicHeight(
+ measurables: List<IntrinsicMeasurable>,
+ width: Int
+ ): Int = childMinIntrinsicHeight
+
+ override fun IntrinsicMeasureScope.maxIntrinsicHeight(
+ measurables: List<IntrinsicMeasurable>,
+ width: Int
+ ): Int = childMaxIntrinsicHeight
+ }
+ var minIntrinsicWidth = -1
+ var maxIntrinsicWidth = -1
+ var minIntrinsicHeight = -1
+ var maxIntrinsicHeight = -1
+
+ rule.setContent {
+ Layout(
+ modifier = Modifier
+ .layout { measurable, _ ->
+ minIntrinsicWidth = measurable.minIntrinsicWidth(0)
+ maxIntrinsicWidth = measurable.maxIntrinsicWidth(0)
+ minIntrinsicHeight = measurable.minIntrinsicHeight(0)
+ maxIntrinsicHeight = measurable.maxIntrinsicHeight(0)
+ layout(0, 0) {}
+ }
+ .basicMarqueeWithTestParams(),
+ measurePolicy = fixedIntrinsicsMeasurePolicy
+ )
+ }
+
+ rule.runOnIdle {
+ assertThat(minIntrinsicWidth).isEqualTo(0)
+ assertThat(maxIntrinsicWidth).isEqualTo(childMaxIntrinsicWidth)
+ assertThat(minIntrinsicHeight).isEqualTo(childMinIntrinsicHeight)
+ assertThat(maxIntrinsicHeight).isEqualTo(childMaxIntrinsicHeight)
+ }
+ }
+
+ /** See b/278729564. */
+ @Test
+ fun readingIntrinsicsDoesntCauseMeasureLoop() {
+ var outerMeasures = 0
+
+ rule.setContent {
+ Box(
+ Modifier
+ .width(10.dp)
+ .layout { measurable, constraints ->
+ outerMeasures++
+
+ // Querying the intrinsics should _not_ cause measure to be invalidated on
+ // the next frame.
+ measurable.maxIntrinsicWidth(0)
+
+ val placeable = measurable.measure(constraints)
+ layout(placeable.width, placeable.height) {
+ placeable.place(0, 0)
+ }
+ }
+ .basicMarqueeWithTestParams(
+ iterations = Int.MAX_VALUE,
+ initialDelayMillis = 0,
+ delayMillis = 0,
+ animationMode = Immediately
+ )
+ ) {
+ BasicText(text = "the quick brown fox jumped over the lazy dogs")
+ }
+ }
+
+ rule.runOnIdle {
+ assertThat(outerMeasures).isEqualTo(2)
+ }
+
+ // Let the animation run for a few frames.
+ repeat(10) {
+ rule.mainClock.advanceTimeByFrame()
+ }
+
+ rule.runOnIdle {
+ assertThat(outerMeasures).isEqualTo(2)
+ }
+ }
+
private fun testAnimationContinuity(
resetsAnimation: Boolean,
modifier1: Modifier,
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt
index d0fc90e..603ac21 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt
@@ -94,7 +94,10 @@
@Test
fun measureAndPlaceTwoItems() {
val itemProvider = itemProvider({ 2 }) { index ->
- Box(Modifier.fillMaxSize().testTag("$index"))
+ Box(
+ Modifier
+ .fillMaxSize()
+ .testTag("$index"))
}
rule.setContent {
LazyLayout(itemProvider) {
@@ -118,8 +121,14 @@
@Test
fun measureAndPlaceMultipleLayoutsInOneItem() {
val itemProvider = itemProvider({ 1 }) { index ->
- Box(Modifier.fillMaxSize().testTag("${index}x0"))
- Box(Modifier.fillMaxSize().testTag("${index}x1"))
+ Box(
+ Modifier
+ .fillMaxSize()
+ .testTag("${index}x0"))
+ Box(
+ Modifier
+ .fillMaxSize()
+ .testTag("${index}x1"))
}
rule.setContent {
@@ -143,7 +152,10 @@
@Test
fun updatingitemProvider() {
var itemProvider by mutableStateOf(itemProvider({ 1 }) { index ->
- Box(Modifier.fillMaxSize().testTag("$index"))
+ Box(
+ Modifier
+ .fillMaxSize()
+ .testTag("$index"))
})
rule.setContent {
@@ -166,7 +178,10 @@
rule.runOnIdle {
itemProvider = itemProvider({ 2 }) { index ->
- Box(Modifier.fillMaxSize().testTag("$index"))
+ Box(
+ Modifier
+ .fillMaxSize()
+ .testTag("$index"))
}
}
@@ -178,7 +193,10 @@
fun stateBaseditemProvider() {
var itemCount by mutableStateOf(1)
val itemProvider = itemProvider({ itemCount }) { index ->
- Box(Modifier.fillMaxSize().testTag("$index"))
+ Box(
+ Modifier
+ .fillMaxSize()
+ .testTag("$index"))
}
rule.setContent {
@@ -228,7 +246,11 @@
}
}
val itemProvider = itemProvider({ 1 }) { index ->
- Box(Modifier.fillMaxSize().testTag("$index").then(modifier))
+ Box(
+ Modifier
+ .fillMaxSize()
+ .testTag("$index")
+ .then(modifier))
}
var needToCompose by mutableStateOf(false)
val prefetchState = LazyLayoutPrefetchState()
@@ -335,13 +357,15 @@
fun nodeIsReusedWithoutExtraRemeasure() {
var indexToCompose by mutableStateOf<Int?>(0)
var remeasuresCount = 0
- val modifier = Modifier.layout { measurable, constraints ->
- val placeable = measurable.measure(constraints)
- remeasuresCount++
- layout(placeable.width, placeable.height) {
- placeable.place(0, 0)
+ val modifier = Modifier
+ .layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
+ remeasuresCount++
+ layout(placeable.width, placeable.height) {
+ placeable.place(0, 0)
+ }
}
- }.fillMaxSize()
+ .fillMaxSize()
val itemProvider = itemProvider({ 2 }) {
Box(modifier)
}
@@ -376,6 +400,52 @@
}
@Test
+ fun nodeIsReusedWhenRemovedFirst() {
+ var itemCount by mutableStateOf(1)
+ var remeasuresCount = 0
+ val modifier = Modifier
+ .layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
+ remeasuresCount++
+ layout(placeable.width, placeable.height) {
+ placeable.place(0, 0)
+ }
+ }
+ .fillMaxSize()
+ val itemProvider = itemProvider({ itemCount }) {
+ Box(modifier)
+ }
+
+ rule.setContent {
+ LazyLayout(itemProvider) { constraints ->
+ val node = if (itemCount == 1) {
+ measure(0, constraints).first()
+ } else {
+ null
+ }
+ layout(10, 10) {
+ node?.place(0, 0)
+ }
+ }
+ }
+
+ rule.runOnIdle {
+ assertThat(remeasuresCount).isEqualTo(1)
+ // node will be kept for reuse
+ itemCount = 0
+ }
+
+ rule.runOnIdle {
+ // node should be now reused
+ itemCount = 1
+ }
+
+ rule.runOnIdle {
+ assertThat(remeasuresCount).isEqualTo(1)
+ }
+ }
+
+ @Test
fun regularCompositionIsUsedInPrefetchTimeCalculation() {
val itemProvider = itemProvider({ 1 }) {
Box(Modifier.fillMaxSize())
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListTest.kt
index 6251158..2b0b778 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListTest.kt
@@ -1898,7 +1898,7 @@
}
@Test
- fun scrollingALotDoesntCauseLazyLayoutRecomposition() {
+ fun scrollingToItemDoesntCauseLazyLayoutRecomposition() {
var recomposeCount = 0
lateinit var state: LazyListState
@@ -1959,10 +1959,8 @@
}
}
- repeat(5) {
- rule.onNodeWithTag(LazyListTag)
- .scrollMainAxisBy(50.dp) // 10 items, half a screen
- }
+ rule.onNodeWithTag(LazyListTag)
+ .scrollMainAxisBy(250.dp) // 10 items, half a screen
rule.runOnIdle {
assertThat(composedMoreThanOnce).isZero()
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt
index f4edcff..4dedd01 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt
@@ -873,17 +873,14 @@
.border(1.dp, Color.Red),
) {
items(itemSizes.size) {
- Box(
+ Spacer(
Modifier
.axisSize(
crossAxis = itemSizeDp,
mainAxis = itemSizes[it]
)
.testTag("$it")
- .border(1.dp, Color.Black)
- ) {
- BasicText("$it")
- }
+ )
}
}
}
@@ -984,10 +981,9 @@
Modifier
.axisSize(
crossAxis = itemSizeDp,
- mainAxis = itemSizeDp * (it % 3 + 1)
+ mainAxis = itemSizeDp
)
.testTag("$it")
- .border(1.dp, Color.Black)
)
}
}
@@ -1000,11 +996,12 @@
.assertMainAxisStartPositionInRootIsEqualTo(0.dp)
// check that scrolling back and forth doesn't crash
- rule.onNodeWithTag(LazyStaggeredGridTag)
- .scrollMainAxisBy(1000.dp)
+ val delta = itemSizeDp * 5
+ state.scrollBy(-delta)
- rule.onNodeWithTag(LazyStaggeredGridTag)
- .scrollMainAxisBy(-1000.dp)
+ state.scrollBy(delta * 2)
+
+ state.scrollBy(-delta)
rule.onNodeWithTag("${Int.MAX_VALUE / 2}")
.assertMainAxisStartPositionInRootIsEqualTo(0.dp)
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt
index 80cc4f9..1c1eb34 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt
@@ -57,6 +57,7 @@
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
+import kotlin.test.assertTrue
import kotlinx.coroutines.CoroutineScope
@OptIn(ExperimentalFoundationApi::class)
@@ -336,6 +337,20 @@
rule.onNodeWithTag("$pageToVerifyPosition")
.assertPositionInRootIsEqualTo(left + leftContentPadding, top + topContentPadding)
}
+
+ internal fun runAndWaitForPageSettling(block: () -> Unit) {
+ block()
+ rule.mainClock.advanceTimeUntil {
+ pagerState.currentPageOffsetFraction != 0.0f
+ } // wait for first move from drag
+ rule.mainClock.advanceTimeUntil {
+ pagerState.currentPageOffsetFraction == 0.0f
+ } // wait for fling settling
+ // pump the clock twice and check we're still settled.
+ rule.mainClock.advanceTimeByFrame()
+ rule.mainClock.advanceTimeByFrame()
+ assertTrue { pagerState.currentPageOffsetFraction == 0.0f }
+ }
}
class ParamConfig(
@@ -358,7 +373,7 @@
internal const val PagerTestTag = "pager"
internal const val DefaultPageCount = 20
-internal const val DefaultAnimationRepetition = 3
+internal const val DefaultAnimationRepetition = 2
internal val TestOrientation = listOf(Orientation.Vertical, Orientation.Horizontal)
internal val AllOrientationsParams = mutableListOf<ParamConfig>().apply {
for (orientation in TestOrientation) {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt
index 6531eba..ef8e8fa 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt
@@ -24,6 +24,7 @@
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performTouchInput
import androidx.test.filters.LargeTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -35,6 +36,11 @@
val config: ParamConfig
) : BasePagerTest(config) {
+ @Before
+ fun setUp() {
+ rule.mainClock.autoAdvance = false
+ }
+
@Test
fun swipeForwardAndBackward_verifyPagesAreLaidOutCorrectly() {
// Arrange
@@ -45,13 +51,14 @@
repeat(DefaultAnimationRepetition) {
rule.onNodeWithTag(it.toString()).assertIsDisplayed()
confirmPageIsInCorrectPosition(it)
- rule.onNodeWithTag(it.toString()).performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 1.5f * MinFlingVelocityDp.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ rule.onNodeWithTag(it.toString()).performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 1.5f * MinFlingVelocityDp.toPx() },
+ delta
+ )
+ }
}
- rule.waitForIdle()
}
// Act - backward
@@ -59,13 +66,14 @@
val countDown = DefaultAnimationRepetition - it
rule.onNodeWithTag(countDown.toString()).assertIsDisplayed()
confirmPageIsInCorrectPosition(countDown)
- rule.onNodeWithTag(countDown.toString()).performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 1.5f * MinFlingVelocityDp.toPx() },
- delta * -1f
- )
+ runAndWaitForPageSettling {
+ rule.onNodeWithTag(countDown.toString()).performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 1.5f * MinFlingVelocityDp.toPx() },
+ delta * -1f
+ )
+ }
}
- rule.waitForIdle()
}
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt
index aaf1a6f..5967023 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt
@@ -22,6 +22,7 @@
import androidx.compose.ui.test.performTouchInput
import androidx.test.filters.LargeTest
import com.google.common.truth.Truth
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -33,6 +34,11 @@
val config: ParamConfig
) : BasePagerTest(config) {
+ @Before
+ fun setUp() {
+ rule.mainClock.autoAdvance = false
+ }
+
@Test
fun offscreenPageLimitIsUsed_shouldPlaceMoreItemsThanVisibleOnesAsWeScroll() {
// Arrange
@@ -45,11 +51,12 @@
repeat(DefaultAnimationRepetition) {
// Act
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(0f, delta)
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(0f, delta)
+ }
}
- rule.waitForIdle()
// Next page was placed
rule.runOnIdle {
Truth.assertThat(placed).contains(
@@ -58,7 +65,7 @@
)
}
}
- rule.waitForIdle()
+
confirmPageIsInCorrectPosition(pagerState.currentPage)
}
@@ -102,7 +109,6 @@
)
// Assert
- rule.waitForIdle()
val firstVisible = pagerState.layoutInfo.visiblePagesInfo.first().index
val lastVisible = pagerState.layoutInfo.visiblePagesInfo.last().index
Truth.assertThat(placed).doesNotContain(firstVisible - 1)
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt
index 70cc0a6..103f9c7 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt
@@ -32,6 +32,7 @@
import androidx.test.filters.LargeTest
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -43,6 +44,11 @@
val config: ParamConfig
) : BasePagerTest(config) {
+ @Before
+ fun setUp() {
+ rule.mainClock.autoAdvance = false
+ }
+
@Test
fun swipeWithLowVelocity_positionalThresholdLessThanDefaultThreshold_shouldBounceBack() {
// Arrange
@@ -51,26 +57,28 @@
val delta = pagerSize * swipeValue * scrollForwardSign
// Act - forward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("5").assertIsDisplayed()
confirmPageIsInCorrectPosition(5)
// Act - backward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta * -1
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta * -1
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("5").assertIsDisplayed()
@@ -89,26 +97,28 @@
val delta = pagerSize * swipeValue * scrollForwardSign
// Act - forward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("5").assertIsDisplayed()
confirmPageIsInCorrectPosition(5)
// Act - backward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta * -1
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta * -1
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("5").assertIsDisplayed()
@@ -127,26 +137,28 @@
val delta = pagerSize * swipeValue * scrollForwardSign
// Act - forward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("5").assertIsDisplayed()
confirmPageIsInCorrectPosition(5)
// Act - backward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta * -1
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta * -1
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("5").assertIsDisplayed()
@@ -163,26 +175,28 @@
val delta = (2.4f * pageSize) * scrollForwardSign // 2.4 pages
// Act - forward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("4").assertIsDisplayed()
confirmPageIsInCorrectPosition(4)
// Act - backward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta * -1
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta * -1
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("2").assertIsDisplayed()
@@ -197,26 +211,28 @@
val delta = pagerSize * swipeValue * scrollForwardSign
// Act - forward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("6").assertIsDisplayed()
confirmPageIsInCorrectPosition(6)
// Act - backward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta * -1
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta * -1
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("5").assertIsDisplayed()
@@ -235,26 +251,28 @@
val delta = pagerSize * swipeValue * scrollForwardSign
// Act - forward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("6").assertIsDisplayed()
confirmPageIsInCorrectPosition(6)
// Act - backward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta * -1
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta * -1
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("5").assertIsDisplayed()
@@ -275,26 +293,28 @@
val delta = 2.6f * pageSize * scrollForwardSign
// Act - forward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("5").assertIsDisplayed()
confirmPageIsInCorrectPosition(5)
// Act - backward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta * -1
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta * -1
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("2").assertIsDisplayed()
@@ -313,26 +333,28 @@
val delta = pagerSize * swipeValue * scrollForwardSign
// Act - forward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("6").assertIsDisplayed()
confirmPageIsInCorrectPosition(6)
// Act - backward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
- delta * -1
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * MinFlingVelocityDp.toPx() },
+ delta * -1
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("5").assertIsDisplayed()
@@ -351,26 +373,28 @@
val delta = pagerSize * 0.4f * scrollForwardSign
// Act - forward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * snapVelocityThreshold.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * snapVelocityThreshold.toPx() },
+ delta
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("5").assertIsDisplayed()
confirmPageIsInCorrectPosition(5)
// Act - backward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 0.5f * snapVelocityThreshold.toPx() },
- delta * -1
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * snapVelocityThreshold.toPx() },
+ delta * -1
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("5").assertIsDisplayed()
@@ -385,26 +409,28 @@
val delta = pagerSize * 0.4f * scrollForwardSign
// Act - forward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
+ delta
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("6").assertIsDisplayed()
confirmPageIsInCorrectPosition(6)
// Act - backward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
- delta * -1
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
+ delta * -1
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("5").assertIsDisplayed()
@@ -424,26 +450,28 @@
val delta = pagerSize * 0.4f * scrollForwardSign
// Act - forward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 1.1f * snapVelocityThreshold.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 1.1f * snapVelocityThreshold.toPx() },
+ delta
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("6").assertIsDisplayed()
confirmPageIsInCorrectPosition(6)
// Act - backward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 1.1f * snapVelocityThreshold.toPx() },
- delta * -1
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 1.1f * snapVelocityThreshold.toPx() },
+ delta * -1
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("5").assertIsDisplayed()
@@ -458,26 +486,28 @@
val delta = pagerSize * 0.8f * scrollForwardSign
// Act - forward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
+ delta
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("6").assertIsDisplayed()
confirmPageIsInCorrectPosition(6)
// Act - backward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
- delta * -1
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
+ delta * -1
+ )
+ }
}
- rule.waitForIdle()
// Assert
rule.onNodeWithTag("5").assertIsDisplayed()
@@ -492,10 +522,11 @@
val delta = pagerSize * 1.4f * scrollForwardSign
// Act - forward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(0f, delta)
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(0f, delta)
+ }
}
- rule.waitForIdle()
// Assert
assertThat(pagerState.currentPage).isAtMost(7)
@@ -503,10 +534,11 @@
confirmPageIsInCorrectPosition(pagerState.currentPage)
// Act - backward
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(0f, delta * -1)
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(0f, delta * -1)
+ }
}
- rule.waitForIdle()
// Assert
assertThat(pagerState.currentPage).isAtLeast(5)
@@ -621,8 +653,8 @@
initialPage = initialPage,
initialPageOffsetFraction = 0f
) {
- 10
- }
+ 10
+ }.also { pagerState = it }
}
HorizontalOrVerticalPager(
@@ -636,39 +668,41 @@
}
}
val delta = pageSize * 0.4f * scrollForwardSign
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
+ delta
+ )
+ }
}
-
- rule.waitForIdle()
rule.onNodeWithTag("1").assertIsDisplayed()
confirmPageIsInCorrectPosition(1)
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
+ delta
+ )
+ }
}
- rule.waitForIdle()
rule.onNodeWithTag("2").assertIsDisplayed()
confirmPageIsInCorrectPosition(2)
rule.runOnIdle { initialPage = 1 }
rule.waitForIdle()
- onPager().performTouchInput {
- swipeWithVelocityAcrossMainAxis(
- with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
- delta
- )
+ runAndWaitForPageSettling {
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 1.1f * MinFlingVelocityDp.toPx() },
+ delta
+ )
+ }
}
- rule.waitForIdle()
confirmPageIsInCorrectPosition(2)
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateNonGestureScrollingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateNonGestureScrollingTest.kt
new file mode 100644
index 0000000..7bed3fc
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateNonGestureScrollingTest.kt
@@ -0,0 +1,260 @@
+/*
+ * 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.compose.foundation.pager
+
+import androidx.compose.foundation.AutoTestFrameClock
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.StateRestorationTester
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth
+import kotlin.test.assertFalse
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalFoundationApi::class)
+@LargeTest
+@RunWith(Parameterized::class)
+class PagerStateNonGestureScrollingTest(val config: ParamConfig) : BasePagerTest(config) {
+ @Test
+ fun pagerStateNotAttached_shouldReturnDefaultValues_andChangeAfterAttached() = runBlocking {
+ // Arrange
+ val state = PagerStateImpl(5, 0.2f) { DefaultPageCount }
+
+ Truth.assertThat(state.currentPage).isEqualTo(5)
+ Truth.assertThat(state.currentPageOffsetFraction).isEqualTo(0.2f)
+
+ val currentPage = derivedStateOf { state.currentPage }
+ val currentPageOffsetFraction = derivedStateOf { state.currentPageOffsetFraction }
+
+ rule.setContent {
+ HorizontalOrVerticalPager(
+ state = state,
+ modifier = Modifier
+ .fillMaxSize()
+ .testTag(PagerTestTag)
+ .onSizeChanged { pagerSize = if (vertical) it.height else it.width },
+ pageSize = PageSize.Fill,
+ reverseLayout = config.reverseLayout,
+ pageSpacing = config.pageSpacing,
+ contentPadding = config.mainAxisContentPadding,
+ ) {
+ Page(index = it)
+ }
+ }
+
+ withContext(Dispatchers.Main + AutoTestFrameClock()) {
+ state.scrollToPage(state.currentPage + 1)
+ }
+
+ rule.runOnIdle {
+ Truth.assertThat(currentPage.value).isEqualTo(6)
+ Truth.assertThat(currentPageOffsetFraction.value).isEqualTo(0.0f)
+ }
+ }
+
+ @Test
+ fun initialPageOnPagerState_shouldDisplayThatPageFirst() {
+ // Arrange
+
+ // Act
+ createPager(initialPage = 5, modifier = Modifier.fillMaxSize())
+
+ // Assert
+ rule.onNodeWithTag("4").assertDoesNotExist()
+ rule.onNodeWithTag("5").assertIsDisplayed()
+ rule.onNodeWithTag("6").assertDoesNotExist()
+ confirmPageIsInCorrectPosition(pagerState.currentPage)
+ }
+
+ @Test
+ fun testStateRestoration() {
+ // Arrange
+ val tester = StateRestorationTester(rule)
+ lateinit var state: PagerState
+ tester.setContent {
+ state = rememberPagerState(pageCount = { DefaultPageCount })
+ scope = rememberCoroutineScope()
+ HorizontalOrVerticalPager(
+ state = state,
+ modifier = Modifier.fillMaxSize()
+ ) {
+ Page(it)
+ }
+ }
+
+ // Act
+ rule.runOnIdle {
+ scope.launch {
+ state.scrollToPage(5)
+ }
+ runBlocking {
+ state.scroll {
+ scrollBy(50f)
+ }
+ }
+ }
+
+ val previousPage = state.currentPage
+ val previousOffset = state.currentPageOffsetFraction
+ tester.emulateSavedInstanceStateRestore()
+
+ // Assert
+ rule.runOnIdle {
+ Truth.assertThat(state.currentPage).isEqualTo(previousPage)
+ Truth.assertThat(state.currentPageOffsetFraction).isEqualTo(previousOffset)
+ }
+ }
+
+ @Test
+ fun currentPageOffsetFraction_shouldNeverBeNan() {
+ rule.setContent {
+ val state = rememberPagerState(pageCount = { 10 })
+ // Read state in composition, should never be Nan
+ assertFalse { state.currentPageOffsetFraction.isNaN() }
+ HorizontalOrVerticalPager(state = state) {
+ Page(index = it)
+ }
+ }
+ }
+
+ @Test
+ fun calculatePageCountOffset_shouldBeBasedOnCurrentPage() {
+ val pageToOffsetCalculations = mutableMapOf<Int, Float>()
+ createPager(modifier = Modifier.fillMaxSize(), pageSize = { PageSize.Fixed(20.dp) }) {
+ pageToOffsetCalculations[it] = pagerState.getOffsetFractionForPage(it)
+ Page(index = it)
+ }
+
+ for ((page, offset) in pageToOffsetCalculations) {
+ val currentPage = pagerState.currentPage
+ val currentPageOffset = pagerState.currentPageOffsetFraction
+ Truth.assertThat(offset).isEqualTo((currentPage - page) + currentPageOffset)
+ }
+ }
+
+ @Test
+ fun scrollToPage_usingLaunchedEffect() {
+
+ createPager(additionalContent = {
+ LaunchedEffect(pagerState) {
+ pagerState.scrollToPage(10)
+ }
+ })
+
+ Truth.assertThat(pagerState.currentPage).isEqualTo(10)
+ confirmPageIsInCorrectPosition(10)
+ }
+
+ @Test
+ fun scrollToPageWithOffset_usingLaunchedEffect() {
+ createPager(additionalContent = {
+ LaunchedEffect(pagerState) {
+ pagerState.scrollToPage(10, 0.4f)
+ }
+ })
+
+ Truth.assertThat(pagerState.currentPage).isEqualTo(10)
+ confirmPageIsInCorrectPosition(10, pageOffset = 0.4f)
+ }
+
+ @Test
+ fun animatedScrollToPage_usingLaunchedEffect() {
+
+ createPager(additionalContent = {
+ LaunchedEffect(pagerState) {
+ pagerState.animateScrollToPage(10)
+ }
+ })
+
+ Truth.assertThat(pagerState.currentPage).isEqualTo(10)
+ confirmPageIsInCorrectPosition(10)
+ }
+
+ @Test
+ fun animatedScrollToPageWithOffset_usingLaunchedEffect() {
+
+ createPager(additionalContent = {
+ LaunchedEffect(pagerState) {
+ pagerState.animateScrollToPage(10, 0.4f)
+ }
+ })
+
+ Truth.assertThat(pagerState.currentPage).isEqualTo(10)
+ confirmPageIsInCorrectPosition(10, pageOffset = 0.4f)
+ }
+
+ @Test
+ fun animatedScrollToPage_viewPortNumberOfPages_usingLaunchedEffect_shouldNotPlaceALlPages() {
+
+ createPager(additionalContent = {
+ LaunchedEffect(pagerState) {
+ pagerState.animateScrollToPage(DefaultPageCount - 1)
+ }
+ })
+
+ // Assert
+ rule.runOnIdle {
+ Truth.assertThat(pagerState.currentPage).isEqualTo(DefaultPageCount - 1)
+ Truth.assertThat(placed).doesNotContain(DefaultPageCount / 2 - 1)
+ Truth.assertThat(placed).doesNotContain(DefaultPageCount / 2)
+ Truth.assertThat(placed).doesNotContain(DefaultPageCount / 2 + 1)
+ }
+ confirmPageIsInCorrectPosition(pagerState.currentPage)
+ }
+
+ @Test
+ fun scrollTo_beforeFirstLayout_shouldWaitForStateAndLayoutSetting() {
+ // Arrange
+
+ rule.mainClock.autoAdvance = false
+
+ // Act
+ createPager(modifier = Modifier.fillMaxSize(), additionalContent = {
+ LaunchedEffect(pagerState) {
+ pagerState.scrollToPage(5)
+ }
+ })
+
+ // Assert
+ Truth.assertThat(pagerState.currentPage).isEqualTo(5)
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun params() = mutableListOf<ParamConfig>().apply {
+ for (orientation in TestOrientation) {
+ add(ParamConfig(orientation = orientation))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt
index 9bf833a..fbcbe8b 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt
@@ -22,21 +22,13 @@
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.junit4.StateRestorationTester
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performTouchInput
-import androidx.compose.ui.unit.dp
import androidx.test.filters.LargeTest
import androidx.test.filters.SdkSuppress
import com.google.common.truth.Truth.assertThat
-import kotlin.test.assertFalse
import kotlin.test.assertTrue
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -52,43 +44,6 @@
class PagerStateTest(val config: ParamConfig) : BasePagerTest(config) {
@Test
- fun pagerStateNotAttached_shouldReturnDefaultValues_andChangeAfterAttached() = runBlocking {
- // Arrange
- val state = PagerStateImpl(5, 0.2f) { DefaultPageCount }
-
- assertThat(state.currentPage).isEqualTo(5)
- assertThat(state.currentPageOffsetFraction).isEqualTo(0.2f)
-
- val currentPage = derivedStateOf { state.currentPage }
- val currentPageOffsetFraction = derivedStateOf { state.currentPageOffsetFraction }
-
- rule.setContent {
- HorizontalOrVerticalPager(
- state = state,
- modifier = Modifier
- .fillMaxSize()
- .testTag(PagerTestTag)
- .onSizeChanged { pagerSize = if (vertical) it.height else it.width },
- pageSize = PageSize.Fill,
- reverseLayout = config.reverseLayout,
- pageSpacing = config.pageSpacing,
- contentPadding = config.mainAxisContentPadding,
- ) {
- Page(index = it)
- }
- }
-
- withContext(Dispatchers.Main + AutoTestFrameClock()) {
- state.scrollToPage(state.currentPage + 1)
- }
-
- rule.runOnIdle {
- assertThat(currentPage.value).isEqualTo(6)
- assertThat(currentPageOffsetFraction.value).isEqualTo(0.0f)
- }
- }
-
- @Test
fun scrollToPage_shouldPlacePagesCorrectly() = runBlocking {
// Arrange
createPager(modifier = Modifier.fillMaxSize())
@@ -96,9 +51,11 @@
// Act and Assert
repeat(DefaultAnimationRepetition) {
assertThat(pagerState.currentPage).isEqualTo(it)
+ val nextPage = pagerState.currentPage + 1
withContext(Dispatchers.Main + AutoTestFrameClock()) {
- pagerState.scrollToPage(pagerState.currentPage + 1)
+ pagerState.scrollToPage(nextPage)
}
+ rule.mainClock.advanceTimeUntil { pagerState.currentPage == nextPage }
confirmPageIsInCorrectPosition(pagerState.currentPage)
}
}
@@ -179,9 +136,11 @@
// Act and Assert
repeat(DefaultAnimationRepetition) {
assertThat(pagerState.currentPage).isEqualTo(it)
+ val nextPage = pagerState.currentPage + 1
withContext(Dispatchers.Main + AutoTestFrameClock()) {
- pagerState.animateScrollToPage(pagerState.currentPage + 1)
+ pagerState.animateScrollToPage(nextPage)
}
+ rule.mainClock.advanceTimeUntil { pagerState.currentPage == nextPage }
confirmPageIsInCorrectPosition(pagerState.currentPage)
}
}
@@ -277,31 +236,6 @@
}
@Test
- fun scrollToPage_usingLaunchedEffect() {
-
- createPager(additionalContent = {
- LaunchedEffect(pagerState) {
- pagerState.scrollToPage(10)
- }
- })
- rule.waitForIdle()
- assertThat(pagerState.currentPage).isEqualTo(10)
- confirmPageIsInCorrectPosition(10)
- }
-
- @Test
- fun scrollToPageWithOffset_usingLaunchedEffect() {
- createPager(additionalContent = {
- LaunchedEffect(pagerState) {
- pagerState.scrollToPage(10, 0.4f)
- }
- })
- rule.waitForIdle()
- assertThat(pagerState.currentPage).isEqualTo(10)
- confirmPageIsInCorrectPosition(10, pageOffset = 0.4f)
- }
-
- @Test
fun animateScrollToPage_shouldCoerceWithinRange() = runBlocking {
// Arrange
@@ -345,69 +279,26 @@
@Test
fun animateScrollToPage_withPassedAnimation() = runBlocking {
// Arrange
-
+ rule.mainClock.autoAdvance = false
createPager(modifier = Modifier.fillMaxSize())
val differentAnimation: AnimationSpec<Float> = tween()
// Act and Assert
repeat(DefaultAnimationRepetition) {
assertThat(pagerState.currentPage).isEqualTo(it)
+ val nextPage = pagerState.currentPage + 1
withContext(Dispatchers.Main + AutoTestFrameClock()) {
pagerState.animateScrollToPage(
- pagerState.currentPage + 1,
+ nextPage,
animationSpec = differentAnimation
)
}
+ rule.mainClock.advanceTimeUntil { pagerState.currentPage == nextPage }
confirmPageIsInCorrectPosition(pagerState.currentPage)
}
}
@Test
- fun animatedScrollToPage_usingLaunchedEffect() {
-
- createPager(additionalContent = {
- LaunchedEffect(pagerState) {
- pagerState.animateScrollToPage(10)
- }
- })
- rule.waitForIdle()
- assertThat(pagerState.currentPage).isEqualTo(10)
- confirmPageIsInCorrectPosition(10)
- }
-
- @Test
- fun animatedScrollToPageWithOffset_usingLaunchedEffect() {
-
- createPager(additionalContent = {
- LaunchedEffect(pagerState) {
- pagerState.animateScrollToPage(10, 0.4f)
- }
- })
- rule.waitForIdle()
- assertThat(pagerState.currentPage).isEqualTo(10)
- confirmPageIsInCorrectPosition(10, pageOffset = 0.4f)
- }
-
- @Test
- fun animatedScrollToPage_viewPortNumberOfPages_usingLaunchedEffect_shouldNotPlaceALlPages() {
-
- createPager(additionalContent = {
- LaunchedEffect(pagerState) {
- pagerState.animateScrollToPage(DefaultPageCount - 1)
- }
- })
- rule.waitForIdle()
- // Assert
- rule.runOnIdle {
- assertThat(pagerState.currentPage).isEqualTo(DefaultPageCount - 1)
- assertThat(placed).doesNotContain(DefaultPageCount / 2 - 1)
- assertThat(placed).doesNotContain(DefaultPageCount / 2)
- assertThat(placed).doesNotContain(DefaultPageCount / 2 + 1)
- }
- confirmPageIsInCorrectPosition(pagerState.currentPage)
- }
-
- @Test
fun currentPage_shouldChangeWhenClosestPageToSnappedPositionChanges() {
// Arrange
@@ -783,103 +674,6 @@
}
}
- @Test
- fun initialPageOnPagerState_shouldDisplayThatPageFirst() {
- // Arrange
-
- // Act
- createPager(initialPage = 5, modifier = Modifier.fillMaxSize())
-
- // Assert
- rule.onNodeWithTag("4").assertDoesNotExist()
- rule.onNodeWithTag("5").assertIsDisplayed()
- rule.onNodeWithTag("6").assertDoesNotExist()
- confirmPageIsInCorrectPosition(pagerState.currentPage)
- }
-
- @Test
- fun testStateRestoration() {
- // Arrange
- val tester = StateRestorationTester(rule)
- lateinit var state: PagerState
- tester.setContent {
- state = rememberPagerState(pageCount = { DefaultPageCount })
- scope = rememberCoroutineScope()
- HorizontalOrVerticalPager(
- state = state,
- modifier = Modifier.fillMaxSize()
- ) {
- Page(it)
- }
- }
-
- // Act
- rule.runOnIdle {
- scope.launch {
- state.scrollToPage(5)
- }
- runBlocking {
- state.scroll {
- scrollBy(50f)
- }
- }
- }
-
- val previousPage = state.currentPage
- val previousOffset = state.currentPageOffsetFraction
- tester.emulateSavedInstanceStateRestore()
-
- // Assert
- rule.runOnIdle {
- assertThat(state.currentPage).isEqualTo(previousPage)
- assertThat(state.currentPageOffsetFraction).isEqualTo(previousOffset)
- }
- }
-
- @Test
- fun scrollTo_beforeFirstLayout_shouldWaitForStateAndLayoutSetting() {
- // Arrange
-
- rule.mainClock.autoAdvance = false
-
- // Act
- createPager(modifier = Modifier.fillMaxSize(), additionalContent = {
- LaunchedEffect(pagerState) {
- pagerState.scrollToPage(5)
- }
- })
-
- // Assert
- assertThat(pagerState.currentPage).isEqualTo(5)
- }
-
- @Test
- fun currentPageOffsetFraction_shouldNeverBeNan() {
- rule.setContent {
- val state = rememberPagerState(pageCount = { 10 })
- // Read state in composition, should never be Nan
- assertFalse { state.currentPageOffsetFraction.isNaN() }
- HorizontalOrVerticalPager(state = state) {
- Page(index = it)
- }
- }
- }
-
- @Test
- fun calculatePageCountOffset_shouldBeBasedOnCurrentPage() {
- val pageToOffsetCalculations = mutableMapOf<Int, Float>()
- createPager(modifier = Modifier.fillMaxSize(), pageSize = { PageSize.Fixed(20.dp) }) {
- pageToOffsetCalculations[it] = pagerState.getOffsetFractionForPage(it)
- Page(index = it)
- }
-
- for ((page, offset) in pageToOffsetCalculations) {
- val currentPage = pagerState.currentPage
- val currentPageOffset = pagerState.currentPageOffsetFraction
- assertThat(offset).isEqualTo((currentPage - page) + currentPageOffset)
- }
- }
-
companion object {
@JvmStatic
@Parameterized.Parameters(name = "{0}")
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/BasicTextScreenshotTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/BasicTextScreenshotTest.kt
index b10c06a..8bf92f2 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/BasicTextScreenshotTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/BasicTextScreenshotTest.kt
@@ -21,7 +21,6 @@
import androidx.compose.testutils.assertAgainstGolden
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.captureToImage
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
@@ -43,7 +42,6 @@
@MediumTest
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-@OptIn(ExperimentalTestApi::class)
class BasicTextScreenshotTest {
@get:Rule
val rule = createComposeRule()
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/NodeInvalidationTestParent.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/NodeInvalidationTestParent.kt
index 4776166..8bfbd4d 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/NodeInvalidationTestParent.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/NodeInvalidationTestParent.kt
@@ -18,7 +18,7 @@
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.graphics.ColorProducer
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.createFontFamilyResolver
@@ -43,7 +43,28 @@
assertThat(textChange).isFalse()
}
- @OptIn(ExperimentalTextApi::class)
+ @Test
+ fun colorChanged_usingLambda_doesInvalidateDraw() {
+ val params = generateParams()
+ val redFactory = { Color.Red }
+ val blueFactory = { Color.Blue }
+ val drawParams = DrawParams(params.style, redFactory)
+ val subject = createSubject(params, drawParams)
+ val drawChanged = subject.updateDrawArgs(drawParams.copy(color = blueFactory))
+ assertThat(drawChanged).isTrue()
+ }
+
+ @Test
+ fun colorChanged_usingStyle_doesInvalidateDraw() {
+ val params = generateParams()
+ val drawParams = DrawParams(params.style, { Color.Unspecified })
+ val subject = createSubject(params, drawParams)
+ val drawChanged = subject.updateDrawArgs(
+ drawParams = drawParams.copy(style = drawParams.style.copy(color = Color.Red))
+ )
+ assertThat(drawChanged).isTrue()
+ }
+
@Test
fun brushChange_doesNotInvalidateLayout() {
val params = generateParams()
@@ -121,8 +142,10 @@
assertThat(textChange).isFalse()
}
+ abstract fun Any.updateDrawArgs(drawParams: DrawParams): Boolean
abstract fun Any.updateAll(params: Params): Pair<Boolean, Boolean>
abstract fun createSubject(params: Params): Any
+ abstract fun createSubject(params: Params, drawParams: DrawParams): Any
private fun generateParams(): Params {
return Params(
"text",
@@ -144,4 +167,11 @@
val maxLines: Int,
val minLines: Int
)
+
+ data class DrawParams(
+ val style: TextStyle,
+ val color: ColorProducer? = null,
+ val brush: Brush? = null,
+ val alpha: Float = Float.NaN
+ )
}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNodeInvalidationTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNodeInvalidationTest.kt
index 3a9f492..401c40a 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNodeInvalidationTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNodeInvalidationTest.kt
@@ -32,6 +32,11 @@
)
}
+ override fun Any.updateDrawArgs(drawParams: DrawParams): Boolean {
+ this as TextAnnotatedStringNode
+ return updateDraw(drawParams.color, drawParams.style)
+ }
+
override fun createSubject(params: Params): Any {
return TextAnnotatedStringNode(
text = AnnotatedString(text = params.text),
@@ -44,4 +49,18 @@
minLines = params.minLines
)
}
+
+ override fun createSubject(params: Params, drawParams: DrawParams): Any {
+ return TextAnnotatedStringNode(
+ text = AnnotatedString(text = params.text),
+ style = params.style,
+ fontFamilyResolver = params.fontFamilyResolver,
+ onTextLayout = null,
+ overflow = params.overflow,
+ softWrap = params.softWrap,
+ maxLines = params.maxLines,
+ minLines = params.minLines,
+ overrideColor = drawParams.color
+ )
+ }
}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNodeInvalidationTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNodeInvalidationTest.kt
index 9453f41..a1adb5f 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNodeInvalidationTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNodeInvalidationTest.kt
@@ -17,6 +17,11 @@
package androidx.compose.foundation.text.modifiers
class TextStringSimpleNodeInvalidationTest : NodeInvalidationTestParent() {
+ override fun Any.updateDrawArgs(drawParams: DrawParams): Boolean {
+ this as TextStringSimpleNode
+ return this.updateDraw(drawParams.color, drawParams.style)
+ }
+
override fun Any.updateAll(params: Params): Pair<Boolean, Boolean> {
this as TextStringSimpleNode
return updateText(params.text) to updateLayoutRelatedArgs(
@@ -40,4 +45,17 @@
params.minLines
)
}
+
+ override fun createSubject(params: Params, drawParams: DrawParams): Any {
+ return TextStringSimpleNode(
+ params.text,
+ params.style,
+ params.fontFamilyResolver,
+ params.overflow,
+ params.softWrap,
+ params.maxLines,
+ params.minLines,
+ drawParams.color
+ )
+ }
}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt
index 910e79f..748d6b1 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt
@@ -89,7 +89,7 @@
)
private class BackgroundElement(
- private val color: Color? = null,
+ private val color: Color = Color.Unspecified,
private val brush: Brush? = null,
private val alpha: Float,
private val shape: Shape,
@@ -116,7 +116,7 @@
}
override fun hashCode(): Int {
- var result = color?.hashCode() ?: 0
+ var result = color.hashCode()
result = 31 * result + (brush?.hashCode() ?: 0)
result = 31 * result + alpha.hashCode()
result = 31 * result + shape.hashCode()
@@ -133,7 +133,7 @@
}
private class BackgroundNode(
- var color: Color?,
+ var color: Color,
var brush: Brush?,
var alpha: Float,
var shape: Shape,
@@ -155,7 +155,7 @@
}
private fun ContentDrawScope.drawRect() {
- color?.let { drawRect(color = it) }
+ if (color != Color.Unspecified) drawRect(color = color)
brush?.let { drawRect(brush = it, alpha = alpha) }
}
@@ -166,7 +166,7 @@
} else {
shape.createOutline(size, layoutDirection, this)
}
- color?.let { drawOutline(outline, color = it) }
+ if (color != Color.Unspecified) drawOutline(outline, color = color)
brush?.let { drawOutline(outline, brush = it, alpha = alpha) }
lastOutline = outline
lastSize = size
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt
index 048508f..61d3630 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt
@@ -29,34 +29,35 @@
import androidx.compose.foundation.FixedMotionDurationScale.scaleFactor
import androidx.compose.foundation.MarqueeAnimationMode.Companion.Immediately
import androidx.compose.foundation.MarqueeAnimationMode.Companion.WhileFocused
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.MotionDurationScale
-import androidx.compose.ui.composed
-import androidx.compose.ui.draw.DrawModifier
+import androidx.compose.ui.focus.FocusEventModifierNode
import androidx.compose.ui.focus.FocusState
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.clipRect
import androidx.compose.ui.graphics.drawscope.translate
+import androidx.compose.ui.layout.IntrinsicMeasurable
+import androidx.compose.ui.layout.IntrinsicMeasureScope
import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.layout.LayoutModifier
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.requireDensity
+import androidx.compose.ui.node.requireLayoutDirection
+import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.LayoutDirection.Ltr
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.constrainWidth
import androidx.compose.ui.unit.dp
import kotlin.math.absoluteValue
@@ -64,6 +65,7 @@
import kotlin.math.roundToInt
import kotlin.math.sign
import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
// From https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/widget/TextView.java;l=736;drc=6d97d6d7215fef247d1a90e05545cac3676f9212
@@ -138,8 +140,24 @@
initialDelayMillis: Int = if (animationMode == Immediately) delayMillis else 0,
spacing: MarqueeSpacing = DefaultMarqueeSpacing,
velocity: Dp = DefaultMarqueeVelocity
-): Modifier = composed(
- inspectorInfo = debugInspectorInfo {
+): Modifier = this then MarqueeModifierElement(
+ iterations = iterations,
+ animationMode = animationMode,
+ delayMillis = delayMillis,
+ initialDelayMillis = initialDelayMillis,
+ spacing = spacing,
+ velocity = velocity,
+)
+
+private data class MarqueeModifierElement(
+ private val iterations: Int,
+ private val animationMode: MarqueeAnimationMode,
+ private val delayMillis: Int,
+ private val initialDelayMillis: Int,
+ private val spacing: MarqueeSpacing,
+ private val velocity: Dp,
+) : ModifierNodeElement<MarqueeModifierNode>() {
+ override fun InspectorInfo.inspectableProperties() {
name = "basicMarquee"
properties["iterations"] = iterations
properties["animationMode"] = animationMode
@@ -148,60 +166,92 @@
properties["spacing"] = spacing
properties["velocity"] = velocity
}
-) {
- val density = LocalDensity.current
- val layoutDirection = LocalLayoutDirection.current
- val modifier = remember(
- iterations,
- delayMillis,
- initialDelayMillis,
- velocity,
- density,
- layoutDirection,
- ) {
- MarqueeModifier(
+
+ override fun create(): MarqueeModifierNode =
+ MarqueeModifierNode(
iterations = iterations,
+ animationMode = animationMode,
delayMillis = delayMillis,
initialDelayMillis = initialDelayMillis,
- velocity = velocity * if (layoutDirection == Ltr) 1f else -1f,
- density = density
+ spacing = spacing,
+ velocity = velocity,
+ )
+
+ override fun update(node: MarqueeModifierNode) {
+ node.update(
+ iterations = iterations,
+ animationMode = animationMode,
+ delayMillis = delayMillis,
+ initialDelayMillis = initialDelayMillis,
+ spacing = spacing,
+ velocity = velocity,
)
}
- modifier.spacing = spacing
- modifier.animationMode = animationMode
-
- LaunchedEffect(modifier) {
- modifier.runAnimation()
- }
-
- return@composed modifier
}
-private class MarqueeModifier(
- private val iterations: Int,
- private val delayMillis: Int,
- private val initialDelayMillis: Int,
- private val velocity: Dp,
- private val density: Density,
-) : Modifier.Element,
- LayoutModifier,
- DrawModifier,
- @Suppress("DEPRECATION") androidx.compose.ui.focus.FocusEventModifier {
+private class MarqueeModifierNode(
+ private var iterations: Int,
+ animationMode: MarqueeAnimationMode,
+ private var delayMillis: Int,
+ private var initialDelayMillis: Int,
+ spacing: MarqueeSpacing,
+ private var velocity: Dp,
+) : Modifier.Node(),
+ LayoutModifierNode,
+ DrawModifierNode,
+ FocusEventModifierNode {
private var contentWidth by mutableStateOf(0)
private var containerWidth by mutableStateOf(0)
private var hasFocus by mutableStateOf(false)
- var spacing: MarqueeSpacing by mutableStateOf(DefaultMarqueeSpacing)
- var animationMode: MarqueeAnimationMode by mutableStateOf(Immediately)
+ var spacing: MarqueeSpacing by mutableStateOf(spacing)
+ var animationMode: MarqueeAnimationMode by mutableStateOf(animationMode)
private val offset = Animatable(0f)
- private val direction = sign(velocity.value)
+ private val direction
+ get() = sign(velocity.value) * when (requireLayoutDirection()) {
+ LayoutDirection.Ltr -> 1
+ LayoutDirection.Rtl -> -1
+ }
private val spacingPx by derivedStateOf {
with(spacing) {
- density.calculateSpacing(contentWidth, containerWidth)
+ requireDensity().calculateSpacing(contentWidth, containerWidth)
}
}
+ override fun onAttach() {
+ restartAnimation()
+ }
+
+ fun update(
+ iterations: Int,
+ animationMode: MarqueeAnimationMode,
+ delayMillis: Int,
+ initialDelayMillis: Int,
+ spacing: MarqueeSpacing,
+ velocity: Dp,
+ ) {
+ this.spacing = spacing
+ this.animationMode = animationMode
+
+ if (
+ this.iterations != iterations ||
+ this.delayMillis != delayMillis ||
+ this.initialDelayMillis != initialDelayMillis ||
+ this.velocity != velocity
+ ) {
+ this.iterations = iterations
+ this.delayMillis = delayMillis
+ this.initialDelayMillis = initialDelayMillis
+ this.velocity = velocity
+ restartAnimation()
+ }
+ }
+
+ override fun onFocusEvent(focusState: FocusState) {
+ hasFocus = focusState.hasFocus
+ }
+
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
@@ -217,6 +267,31 @@
}
}
+ // Override intrinsic calculations to avoid setting state (see b/278729564).
+
+ /** Always returns zero since the marquee has no minimum width. */
+ override fun IntrinsicMeasureScope.minIntrinsicWidth(
+ measurable: IntrinsicMeasurable,
+ height: Int
+ ): Int = 0
+
+ override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+ measurable: IntrinsicMeasurable,
+ height: Int
+ ): Int = measurable.maxIntrinsicWidth(height)
+
+ /** Ignores width since marquee contents are always measured with infinite width. */
+ override fun IntrinsicMeasureScope.minIntrinsicHeight(
+ measurable: IntrinsicMeasurable,
+ width: Int
+ ): Int = measurable.minIntrinsicHeight(Constraints.Infinity)
+
+ /** Ignores width since marquee contents are always measured with infinite width. */
+ override fun IntrinsicMeasureScope.maxIntrinsicHeight(
+ measurable: IntrinsicMeasurable,
+ width: Int
+ ): Int = measurable.maxIntrinsicHeight(Constraints.Infinity)
+
override fun ContentDrawScope.draw() {
val clipOffset = offset.value * direction
val firstCopyVisible = when (direction) {
@@ -249,11 +324,15 @@
}
}
- override fun onFocusEvent(focusState: FocusState) {
- hasFocus = focusState.hasFocus
+ private fun restartAnimation() {
+ if (isAttached) {
+ coroutineScope.launch {
+ runAnimation()
+ }
+ }
}
- suspend fun runAnimation() {
+ private suspend fun runAnimation() {
if (iterations <= 0) {
// No animation.
return
@@ -279,7 +358,7 @@
initialDelayMillis,
delayMillis,
velocity,
- density
+ requireDensity()
)
offset.snapTo(0f)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
index 5ef5aa7..86fda0b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
@@ -46,7 +46,7 @@
import androidx.compose.ui.platform.debugInspectorInfo
import androidx.compose.ui.platform.inspectable
import androidx.compose.ui.semantics.Role
-import androidx.compose.ui.semantics.SemanticsConfiguration
+import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.semantics.disabled
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.semantics.onLongClick
@@ -645,7 +645,7 @@
private var onClickLabel: String?,
private var role: Role?,
private var onClick: () -> Unit
-) : DelegatingNode(), SemanticsModifierNode, PointerInputModifierNode, KeyInputModifierNode {
+) : DelegatingNode(), PointerInputModifierNode, KeyInputModifierNode {
abstract val clickablePointerInputNode: AbstractClickablePointerInputNode
abstract val clickableSemanticsNode: ClickableSemanticsNode
@@ -695,9 +695,6 @@
interactionData.currentKeyPressInteractions.clear()
}
- override val semanticsConfiguration: SemanticsConfiguration
- get() = clickableSemanticsNode.semanticsConfiguration
-
override fun onPointerEvent(
pointerEvent: PointerEvent,
pass: PointerEventPass,
@@ -812,26 +809,26 @@
this.onLongClick = onLongClick
}
- override val semanticsConfiguration
- get() = SemanticsConfiguration().apply {
- isMergingSemanticsOfDescendants = true
- if (this@ClickableSemanticsNode.role != null) {
- role = this@ClickableSemanticsNode.role!!
- }
- onClick(
- action = { onClick(); true },
- label = onClickLabel
- )
- if (onLongClick != null) {
- onLongClick(
- action = { onLongClick?.invoke(); true },
- label = onLongClickLabel
- )
- }
- if (!enabled) {
- disabled()
- }
+ override val shouldMergeDescendantSemantics: Boolean
+ get() = true
+ override fun SemanticsPropertyReceiver.applySemantics() {
+ if (this@ClickableSemanticsNode.role != null) {
+ role = this@ClickableSemanticsNode.role!!
}
+ onClick(
+ action = { onClick(); true },
+ label = onClickLabel
+ )
+ if (onLongClick != null) {
+ onLongClick(
+ action = { onLongClick?.invoke(); true },
+ label = onLongClickLabel
+ )
+ }
+ if (!enabled) {
+ disabled()
+ }
+ }
}
private sealed class AbstractClickablePointerInputNode(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
index ff1bcc1..de22bba 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
@@ -51,6 +51,7 @@
import androidx.compose.ui.platform.debugInspectorInfo
import androidx.compose.ui.platform.inspectable
import androidx.compose.ui.semantics.SemanticsConfiguration
+import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.semantics.focused
import androidx.compose.ui.semantics.requestFocus
import kotlinx.coroutines.launch
@@ -153,7 +154,7 @@
private val inputModeManager: InputModeManager
get() = currentValueOf(LocalInputModeManager)
- override fun modifyFocusProperties(focusProperties: FocusProperties) {
+ override fun applyFocusProperties(focusProperties: FocusProperties) {
focusProperties.apply {
canFocus = inputModeManager.inputMode != InputMode.Touch
}
@@ -246,9 +247,9 @@
}
// TODO(levima) Remove this once delegation can propagate this events on its own
- override val semanticsConfiguration: SemanticsConfiguration
- get() = focusableSemanticsNode.semanticsConfiguration
-
+ override fun SemanticsPropertyReceiver.applySemantics() {
+ with(focusableSemanticsNode) { applySemantics() }
+ }
// TODO(levima) Remove this once delegation can propagate this events on its own
override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
focusedBoundsNode.onGloballyPositioned(coordinates)
@@ -362,11 +363,10 @@
this.isFocused = focused
}
- override val semanticsConfiguration: SemanticsConfiguration
- get() = semanticsConfigurationCache.apply {
- focused = isFocused
- requestFocus {
- this@FocusableSemanticsNode.requestFocus()
- }
+ override fun SemanticsPropertyReceiver.applySemantics() {
+ focused = isFocused
+ requestFocus {
+ this@FocusableSemanticsNode.requestFocus()
}
+ }
}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
index e550d27..0ce2c64 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
@@ -32,6 +32,7 @@
import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@@ -124,7 +125,7 @@
internal val internalInteractionSource: MutableInteractionSource = MutableInteractionSource()
- private var _maxValueState = mutableStateOf(Int.MAX_VALUE)
+ private var _maxValueState = mutableIntStateOf(Int.MAX_VALUE)
/**
* We receive scroll events in floats but represent the scroll position in ints so we have to
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScopeImpl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScopeImpl.kt
index 16639c9..48c3e74 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScopeImpl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScopeImpl.kt
@@ -20,7 +20,7 @@
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.lazy.layout.LazyLayoutAnimateItemModifierNode
import androidx.compose.runtime.State
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasureResult
@@ -37,8 +37,8 @@
internal class LazyItemScopeImpl : LazyItemScope {
- private var maxWidthState = mutableStateOf(Int.MAX_VALUE)
- private var maxHeightState = mutableStateOf(Int.MAX_VALUE)
+ private var maxWidthState = mutableIntStateOf(Int.MAX_VALUE)
+ private var maxHeightState = mutableIntStateOf(Int.MAX_VALUE)
fun setMaxSize(width: Int, height: Int) {
maxWidthState.intValue = width
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt
index 4023a81..9b61189 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt
@@ -19,6 +19,7 @@
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.ReusableContentHost
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -95,13 +96,11 @@
val index = itemProvider.findIndexByKey(key, lastKnownIndex).also {
lastKnownIndex = it
}
-
- if (index < itemProvider.itemCount) {
- val key = itemProvider.getKey(index)
- if (key == this.key) {
- StableSaveProvider(StableValue(saveableStateHolder), StableValue(key)) {
- itemProvider.Item(index)
- }
+ val indexIsUpToDate =
+ index < itemProvider.itemCount && itemProvider.getKey(index) == key
+ ReusableContentHost(active = indexIsUpToDate) {
+ StableSaveProvider(StableValue(saveableStateHolder), StableValue(key)) {
+ itemProvider.Item(index)
}
}
DisposableEffect(key) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
index 025ea33..23c078e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
@@ -70,8 +70,9 @@
* @see androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider for the implementation
* of a [SnapLayoutInfoProvider] that uses [androidx.compose.foundation.lazy.LazyListState].
*
- * Please refer to the sample to learn how to use this API.
+ * Please refer to the samples to learn how to use this API.
* @sample androidx.compose.foundation.samples.SimpleHorizontalPagerSample
+ * @sample androidx.compose.foundation.samples.HorizontalPagerWithScrollableContent
*
* @param state The state to control this pager
* @param modifier A modifier instance to be applied to this Pager outer layout
@@ -180,7 +181,8 @@
*/
@Deprecated(
"Please use the overload without pageCount. pageCount should be provided " +
- "through PagerState.", ReplaceWith(
+ "through PagerState.",
+ ReplaceWith(
"""HorizontalPager(
modifier = modifier,
state = state,
@@ -201,8 +203,9 @@
"androidx.compose.foundation.layout.PaddingValues",
"androidx.compose.foundation.pager.PageSize",
"androidx.compose.foundation.pager.PagerDefaults"
- )
- )
+ ),
+ ),
+ level = DeprecationLevel.ERROR
)
@Composable
@ExperimentalFoundationApi
@@ -386,7 +389,8 @@
"androidx.compose.foundation.pager.PageSize",
"androidx.compose.foundation.pager.PagerDefaults"
)
- )
+ ),
+ level = DeprecationLevel.ERROR
)
@Composable
@ExperimentalFoundationApi
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
index 23e45b8..1e9132b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
@@ -103,7 +103,7 @@
){
// provide pageCount
}"""
- )
+ ), level = DeprecationLevel.ERROR
)
@ExperimentalFoundationApi
@Composable
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt
index a623181..45409ef 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt
@@ -32,6 +32,7 @@
import androidx.compose.runtime.saveable.Saver
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.ColorProducer
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Measurable
@@ -73,8 +74,8 @@
* [overflow] and [softWrap]. It is required that 1 <= [minLines] <= [maxLines].
* @param minLines The minimum height in terms of minimum number of visible lines. It is required
* that 1 <= [minLines] <= [maxLines].
+ * @param color Overrides the text color provided in [style]
*/
-@OptIn(InternalFoundationTextApi::class)
@Composable
fun BasicText(
text: String,
@@ -84,7 +85,8 @@
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
- minLines: Int = 1
+ minLines: Int = 1,
+ color: ColorProducer? = null
) {
validateMinMaxLines(
minLines = minLines,
@@ -117,7 +119,8 @@
fontFamilyResolver = LocalFontFamilyResolver.current,
placeholders = null,
onPlaceholderLayout = null,
- selectionController = selectionController
+ selectionController = selectionController,
+ color = color
)
} else {
modifier
@@ -129,7 +132,8 @@
overflow = overflow,
softWrap = softWrap,
maxLines = maxLines,
- minLines = minLines
+ minLines = minLines,
+ color = color
)
}
Layout(finalModifier, EmptyMeasurePolicy)
@@ -158,8 +162,8 @@
* that 1 <= [minLines] <= [maxLines].
* @param inlineContent A map store composables that replaces certain ranges of the text. It's
* used to insert composables into text layout. Check [InlineTextContent] for more information.
+ * @param color Overrides the text color provided in [style]
*/
-@OptIn(InternalFoundationTextApi::class)
@Composable
fun BasicText(
text: AnnotatedString,
@@ -170,7 +174,8 @@
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
minLines: Int = 1,
- inlineContent: Map<String, InlineTextContent> = mapOf()
+ inlineContent: Map<String, InlineTextContent> = mapOf(),
+ color: ColorProducer? = null
) {
validateMinMaxLines(
minLines = minLines,
@@ -205,7 +210,8 @@
fontFamilyResolver = LocalFontFamilyResolver.current,
placeholders = null,
onPlaceholderLayout = null,
- selectionController = selectionController
+ selectionController = selectionController,
+ color = color
),
EmptyMeasurePolicy
)
@@ -233,7 +239,8 @@
fontFamilyResolver = LocalFontFamilyResolver.current,
placeholders = placeholders,
onPlaceholderLayout = { measuredPlaceholderPositions.value = it },
- selectionController = selectionController
+ selectionController = selectionController,
+ color = color
),
measurePolicy = TextMeasurePolicy { measuredPlaceholderPositions.value }
)
@@ -288,6 +295,43 @@
)
}
+@Deprecated("Maintained for binary compat", level = DeprecationLevel.HIDDEN)
+@Composable
+fun BasicText(
+ text: String,
+ modifier: Modifier = Modifier,
+ style: TextStyle = TextStyle.Default,
+ onTextLayout: ((TextLayoutResult) -> Unit)? = null,
+ overflow: TextOverflow = TextOverflow.Clip,
+ softWrap: Boolean = true,
+ maxLines: Int = Int.MAX_VALUE,
+ minLines: Int = 1
+) = BasicText(text, modifier, style, onTextLayout, overflow, softWrap, maxLines, minLines)
+
+@Deprecated("Maintained for binary compat", level = DeprecationLevel.HIDDEN)
+@Composable
+fun BasicText(
+ text: AnnotatedString,
+ modifier: Modifier = Modifier,
+ style: TextStyle = TextStyle.Default,
+ onTextLayout: ((TextLayoutResult) -> Unit)? = null,
+ overflow: TextOverflow = TextOverflow.Clip,
+ softWrap: Boolean = true,
+ maxLines: Int = Int.MAX_VALUE,
+ minLines: Int = 1,
+ inlineContent: Map<String, InlineTextContent> = mapOf()
+) = BasicText(
+ text = text,
+ modifier = modifier,
+ style = style,
+ onTextLayout = onTextLayout,
+ overflow = overflow,
+ softWrap = softWrap,
+ maxLines = maxLines,
+ minLines = minLines,
+ inlineContent = inlineContent
+)
+
/**
* A custom saver that won't save if no selection is active.
*/
@@ -352,7 +396,8 @@
fontFamilyResolver: FontFamily.Resolver,
placeholders: List<AnnotatedString.Range<Placeholder>>?,
onPlaceholderLayout: ((List<Rect?>) -> Unit)?,
- selectionController: SelectionController?
+ selectionController: SelectionController?,
+ color: ColorProducer?
): Modifier {
if (selectionController == null) {
val staticTextModifier = TextAnnotatedStringElement(
@@ -366,7 +411,8 @@
minLines,
placeholders,
onPlaceholderLayout,
- null
+ null,
+ color
)
return this then Modifier /* selection position */ then staticTextModifier
} else {
@@ -381,7 +427,8 @@
minLines,
placeholders,
onPlaceholderLayout,
- selectionController
+ selectionController,
+ color
)
return this then selectionController.modifier then selectableTextModifier
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringElement.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringElement.kt
index 4d22aeb..a7554de 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringElement.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringElement.kt
@@ -18,6 +18,7 @@
import androidx.compose.foundation.text.DefaultMinLines
import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.ColorProducer
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.text.AnnotatedString
@@ -41,7 +42,8 @@
private val minLines: Int = DefaultMinLines,
private val placeholders: List<AnnotatedString.Range<Placeholder>>? = null,
private val onPlaceholderLayout: ((List<Rect?>) -> Unit)? = null,
- private val selectionController: SelectionController? = null
+ private val selectionController: SelectionController? = null,
+ private val color: ColorProducer? = null
) : ModifierNodeElement<SelectableTextAnnotatedStringNode>() {
override fun create(): SelectableTextAnnotatedStringNode = SelectableTextAnnotatedStringNode(
@@ -55,7 +57,8 @@
minLines,
placeholders,
onPlaceholderLayout,
- selectionController
+ selectionController,
+ color
)
override fun update(
@@ -72,7 +75,8 @@
overflow = overflow,
onTextLayout = onTextLayout,
onPlaceholderLayout = onPlaceholderLayout,
- selectionController = selectionController
+ selectionController = selectionController,
+ color = color
)
}
@@ -82,6 +86,7 @@
if (other !is SelectableTextAnnotatedStringElement) return false
// these three are most likely to actually change
+ if (color != other.color) return false
if (text != other.text) return false
if (style != other.style) return false
if (placeholders != other.placeholders) return false
@@ -113,6 +118,7 @@
result = 31 * result + (placeholders?.hashCode() ?: 0)
result = 31 * result + (onPlaceholderLayout?.hashCode() ?: 0)
result = 31 * result + (selectionController?.hashCode() ?: 0)
+ result = 31 * result + (color?.hashCode() ?: 0)
return result
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringNode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringNode.kt
index cac5e39..3616033 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringNode.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringNode.kt
@@ -18,6 +18,7 @@
import androidx.compose.foundation.text.DefaultMinLines
import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.ColorProducer
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.layout.IntrinsicMeasurable
import androidx.compose.ui.layout.IntrinsicMeasureScope
@@ -29,9 +30,7 @@
import androidx.compose.ui.node.DrawModifierNode
import androidx.compose.ui.node.GlobalPositionAwareModifierNode
import androidx.compose.ui.node.LayoutModifierNode
-import androidx.compose.ui.node.SemanticsModifierNode
import androidx.compose.ui.node.invalidateMeasurement
-import androidx.compose.ui.semantics.SemanticsConfiguration
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.text.TextLayoutResult
@@ -56,9 +55,9 @@
minLines: Int = DefaultMinLines,
placeholders: List<AnnotatedString.Range<Placeholder>>? = null,
onPlaceholderLayout: ((List<Rect?>) -> Unit)? = null,
- private val selectionController: SelectionController? = null
-) : DelegatingNode(), LayoutModifierNode, DrawModifierNode, GlobalPositionAwareModifierNode,
- SemanticsModifierNode {
+ private val selectionController: SelectionController? = null,
+ overrideColor: ColorProducer? = null
+) : DelegatingNode(), LayoutModifierNode, DrawModifierNode, GlobalPositionAwareModifierNode {
private val delegate = delegate(
TextAnnotatedStringNode(
@@ -72,7 +71,8 @@
minLines = minLines,
placeholders = placeholders,
onPlaceholderLayout = onPlaceholderLayout,
- selectionController = selectionController
+ selectionController = selectionController,
+ overrideColor = overrideColor
)
)
@@ -93,9 +93,6 @@
constraints: Constraints
): MeasureResult = delegate.measureNonExtension(this, measurable, constraints)
- override val semanticsConfiguration: SemanticsConfiguration
- get() = delegate.semanticsConfiguration
-
override fun IntrinsicMeasureScope.minIntrinsicWidth(
measurable: IntrinsicMeasurable,
height: Int
@@ -127,9 +124,11 @@
overflow: TextOverflow,
onTextLayout: ((TextLayoutResult) -> Unit)?,
onPlaceholderLayout: ((List<Rect?>) -> Unit)?,
- selectionController: SelectionController?
+ selectionController: SelectionController?,
+ color: ColorProducer?
) {
delegate.doInvalidations(
+ drawChanged = delegate.updateDraw(color, style),
textChanged = delegate.updateText(
text = text
),
@@ -146,7 +145,7 @@
onTextLayout = onTextLayout,
onPlaceholderLayout = onPlaceholderLayout,
selectionController = selectionController
- )
+ ),
)
// we always relayout when we're selectable
invalidateMeasurement()
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringElement.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringElement.kt
index 6232e52..127e996 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringElement.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringElement.kt
@@ -18,6 +18,7 @@
import androidx.compose.foundation.text.DefaultMinLines
import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.ColorProducer
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.text.AnnotatedString
@@ -43,7 +44,8 @@
private val minLines: Int = DefaultMinLines,
private val placeholders: List<AnnotatedString.Range<Placeholder>>? = null,
private val onPlaceholderLayout: ((List<Rect?>) -> Unit)? = null,
- private val selectionController: SelectionController? = null
+ private val selectionController: SelectionController? = null,
+ private val color: ColorProducer? = null
) : ModifierNodeElement<TextAnnotatedStringNode>() {
override fun create(): TextAnnotatedStringNode = TextAnnotatedStringNode(
@@ -57,11 +59,13 @@
minLines,
placeholders,
onPlaceholderLayout,
- selectionController
+ selectionController,
+ color
)
override fun update(node: TextAnnotatedStringNode) {
node.doInvalidations(
+ drawChanged = node.updateDraw(color, style),
textChanged = node.updateText(
text = text
),
@@ -88,7 +92,8 @@
if (other !is TextAnnotatedStringElement) return false
// these three are most likely to actually change
- if (text != other.text) return false
+ if (color != other.color) return false
+ if (text != other.text) return false /* expensive to check, do it after color */
if (style != other.style) return false
if (placeholders != other.placeholders) return false
@@ -119,6 +124,7 @@
result = 31 * result + (placeholders?.hashCode() ?: 0)
result = 31 * result + (onPlaceholderLayout?.hashCode() ?: 0)
result = 31 * result + (selectionController?.hashCode() ?: 0)
+ result = 31 * result + (color?.hashCode() ?: 0)
return result
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt
index 9416b99..54b93c4 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt
@@ -22,6 +22,7 @@
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorProducer
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.Fill
@@ -42,7 +43,7 @@
import androidx.compose.ui.node.invalidateLayer
import androidx.compose.ui.node.invalidateMeasurement
import androidx.compose.ui.node.invalidateSemantics
-import androidx.compose.ui.semantics.SemanticsConfiguration
+import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.semantics.getTextLayoutResult
import androidx.compose.ui.semantics.text
import androidx.compose.ui.text.AnnotatedString
@@ -70,7 +71,8 @@
private var minLines: Int = DefaultMinLines,
private var placeholders: List<AnnotatedString.Range<Placeholder>>? = null,
private var onPlaceholderLayout: ((List<Rect?>) -> Unit)? = null,
- private var selectionController: SelectionController? = null
+ private var selectionController: SelectionController? = null,
+ private var overrideColor: ColorProducer? = null
) : Modifier.Node(), LayoutModifierNode, DrawModifierNode, SemanticsModifierNode {
private var baselineCache: Map<AlignmentLine, Int>? = null
@@ -97,6 +99,19 @@
}
/**
+ * Element has draw parameters to update
+ */
+ fun updateDraw(color: ColorProducer?, style: TextStyle): Boolean {
+ var changed = false
+ if (color != this.overrideColor) {
+ changed = true
+ }
+ overrideColor = color
+ changed = changed || !style.hasSameDrawAffectingAttributes(this.style)
+ return changed
+ }
+
+ /**
* Element has text parameters to update
*/
fun updateText(text: AnnotatedString): Boolean {
@@ -186,12 +201,12 @@
* Do appropriate invalidate calls based on the results of update above.
*/
fun doInvalidations(
+ drawChanged: Boolean,
textChanged: Boolean,
layoutChanged: Boolean,
callbacksChanged: Boolean
) {
if (textChanged) {
- _semanticsConfiguration = null
invalidateSemantics()
}
@@ -207,15 +222,16 @@
placeholders = placeholders
)
invalidateMeasurement()
+ invalidateDraw()
}
- invalidateDraw()
+ if (drawChanged) {
+ invalidateDraw()
+ }
}
- private var _semanticsConfiguration: SemanticsConfiguration? = null
-
private var semanticsTextLayoutResult: ((MutableList<TextLayoutResult>) -> Boolean)? = null
- private fun generateSemantics(text: AnnotatedString): SemanticsConfiguration {
+ override fun SemanticsPropertyReceiver.applySemantics() {
var localSemanticsTextLayoutResult = semanticsTextLayoutResult
if (localSemanticsTextLayoutResult == null) {
localSemanticsTextLayoutResult = { textLayoutResult ->
@@ -226,24 +242,10 @@
}
semanticsTextLayoutResult = localSemanticsTextLayoutResult
}
- return SemanticsConfiguration().also {
- it.isMergingSemanticsOfDescendants = false
- it.isClearingSemantics = false
- it.text = text
- it.getTextLayoutResult(action = localSemanticsTextLayoutResult)
- }
+ text = this@TextAnnotatedStringNode.text
+ getTextLayoutResult(action = localSemanticsTextLayoutResult)
}
- override val semanticsConfiguration: SemanticsConfiguration
- get() {
- var localSemantics = _semanticsConfiguration
- if (localSemantics == null) {
- localSemantics = generateSemantics(text)
- _semanticsConfiguration = localSemantics
- }
- return localSemantics
- }
-
fun measureNonExtension(
measureScope: MeasureScope,
measurable: Measurable,
@@ -382,7 +384,10 @@
decoration = textDecoration
)
} else {
- val color = if (style.color.isSpecified) {
+ val overrideColorVal = overrideColor?.invoke() ?: Color.Unspecified
+ val color = if (overrideColorVal.isSpecified) {
+ overrideColorVal
+ } else if (style.color.isSpecified) {
style.color
} else {
Color.Black
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleElement.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleElement.kt
index bb846b3..85e3272 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleElement.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleElement.kt
@@ -17,6 +17,7 @@
package androidx.compose.foundation.text.modifiers
import androidx.compose.foundation.text.DefaultMinLines
+import androidx.compose.ui.graphics.ColorProducer
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.text.AnnotatedString
@@ -37,6 +38,7 @@
private val softWrap: Boolean = true,
private val maxLines: Int = Int.MAX_VALUE,
private val minLines: Int = DefaultMinLines,
+ private val color: ColorProducer? = null
) : ModifierNodeElement<TextStringSimpleNode>() {
override fun create(): TextStringSimpleNode = TextStringSimpleNode(
@@ -46,11 +48,16 @@
overflow,
softWrap,
maxLines,
- minLines
+ minLines,
+ color
)
override fun update(node: TextStringSimpleNode) {
node.doInvalidations(
+ drawChanged = node.updateDraw(
+ color,
+ style
+ ),
textChanged = node.updateText(
text = text
),
@@ -71,7 +78,8 @@
if (other !is TextStringSimpleElement) return false
// these three are most likely to actually change
- if (text != other.text) return false
+ if (color != other.color) return false
+ if (text != other.text) return false /* expensive to check, do after color */
if (style != other.style) return false
// these are equally unlikely to change
@@ -92,6 +100,7 @@
result = 31 * result + softWrap.hashCode()
result = 31 * result + maxLines
result = 31 * result + minLines
+ result = 31 * result + (color?.hashCode() ?: 0)
return result
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt
index a3e87e0..fccf555 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt
@@ -22,6 +22,7 @@
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorProducer
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.Fill
@@ -42,7 +43,7 @@
import androidx.compose.ui.node.invalidateLayer
import androidx.compose.ui.node.invalidateMeasurement
import androidx.compose.ui.node.invalidateSemantics
-import androidx.compose.ui.semantics.SemanticsConfiguration
+import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.semantics.getTextLayoutResult
import androidx.compose.ui.semantics.text
import androidx.compose.ui.text.AnnotatedString
@@ -69,7 +70,8 @@
private var overflow: TextOverflow = TextOverflow.Clip,
private var softWrap: Boolean = true,
private var maxLines: Int = Int.MAX_VALUE,
- private var minLines: Int = DefaultMinLines
+ private var minLines: Int = DefaultMinLines,
+ private var overrideColor: ColorProducer? = null
) : Modifier.Node(), LayoutModifierNode, DrawModifierNode, SemanticsModifierNode {
private var baselineCache: Map<AlignmentLine, Int>? = null
@@ -94,6 +96,16 @@
return layoutCache.also { it.density = density }
}
+ fun updateDraw(color: ColorProducer?, style: TextStyle): Boolean {
+ var changed = false
+ if (color != this.overrideColor) {
+ changed = true
+ }
+ overrideColor = color
+ changed = changed || !style.hasSameDrawAffectingAttributes(this.style)
+ return changed
+ }
+
/**
* Element has text params to update
*/
@@ -151,11 +163,11 @@
* request invalidate based on the results of [updateText] and [updateLayoutRelatedArgs]
*/
fun doInvalidations(
+ drawChanged: Boolean,
textChanged: Boolean,
layoutChanged: Boolean
) {
if (textChanged) {
- _semanticsConfiguration = null
invalidateSemantics()
}
@@ -170,15 +182,16 @@
minLines = minLines
)
invalidateMeasurement()
+ invalidateDraw()
}
- invalidateDraw()
+ if (drawChanged) {
+ invalidateDraw()
+ }
}
- private var _semanticsConfiguration: SemanticsConfiguration? = null
-
private var semanticsTextLayoutResult: ((MutableList<TextLayoutResult>) -> Boolean)? = null
- private fun generateSemantics(text: String): SemanticsConfiguration {
+ override fun SemanticsPropertyReceiver.applySemantics() {
var localSemanticsTextLayoutResult = semanticsTextLayoutResult
if (localSemanticsTextLayoutResult == null) {
localSemanticsTextLayoutResult = { textLayoutResult ->
@@ -190,24 +203,10 @@
}
semanticsTextLayoutResult = localSemanticsTextLayoutResult
}
- return SemanticsConfiguration().also {
- it.isMergingSemanticsOfDescendants = false
- it.isClearingSemantics = false
- it.text = AnnotatedString(text)
- it.getTextLayoutResult(action = localSemanticsTextLayoutResult)
- }
+ this.text = AnnotatedString(this@TextStringSimpleNode.text)
+ getTextLayoutResult(action = localSemanticsTextLayoutResult)
}
- override val semanticsConfiguration: SemanticsConfiguration
- get() {
- var localSemantics = _semanticsConfiguration
- if (localSemantics == null) {
- localSemantics = generateSemantics(text)
- _semanticsConfiguration = localSemantics
- }
- return localSemantics
- }
-
/**
* Text layout happens here
*/
@@ -301,7 +300,10 @@
textDecoration = textDecoration
)
} else {
- val color = if (style.color.isSpecified) {
+ val overrideColorVal = overrideColor?.invoke() ?: Color.Unspecified
+ val color = if (overrideColorVal.isSpecified) {
+ overrideColorVal
+ } else if (style.color.isSpecified) {
style.color
} else {
Color.Black
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/BasicTextPaparazziTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/BasicTextPaparazziTest.kt
index 2141a88..90c62ed 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/BasicTextPaparazziTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/BasicTextPaparazziTest.kt
@@ -25,9 +25,13 @@
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
@@ -45,6 +49,34 @@
val paparazzi = androidxPaparazzi()
@Test
+ fun colorChangingState_changesColor() {
+ paparazzi.snapshot {
+ var color = remember { mutableStateOf(Color.Red) }
+ BasicText(
+ "ABCD",
+ color = { color.value }
+ )
+ SideEffect {
+ color.value = Color.Yellow
+ }
+ }
+ }
+
+ @Test
+ fun colorChangingState_changesColor_annotatedString() {
+ paparazzi.snapshot {
+ var color = remember { mutableStateOf(Color.Red) }
+ BasicText(
+ AnnotatedString("ABCD"),
+ color = { color.value }
+ )
+ SideEffect {
+ color.value = Color.Yellow
+ }
+ }
+ }
+
+ @Test
fun overflowEllipsis_doesEllipsis_whenInPreferredWrapContent() {
paparazzi.snapshot {
// b/275369323
@@ -99,16 +131,21 @@
CompositionLocalProvider(
LocalLayoutDirection provides LayoutDirection.Rtl
) {
- ConstraintLayout(Modifier.fillMaxWidth().background(Color.Green)) {
+ ConstraintLayout(
+ Modifier
+ .fillMaxWidth()
+ .background(Color.Green)) {
val (title, progressBar, expander) = createRefs()
BasicText(
text = "Locale-aware Text",
- modifier = Modifier.constrainAs(title) {
- top.linkTo(parent.top)
- start.linkTo(parent.start)
- end.linkTo(expander.start)
- width = Dimension.fillToConstraints
- }.border(2.dp, Color.Red)
+ modifier = Modifier
+ .constrainAs(title) {
+ top.linkTo(parent.top)
+ start.linkTo(parent.start)
+ end.linkTo(expander.start)
+ width = Dimension.fillToConstraints
+ }
+ .border(2.dp, Color.Red)
)
Box(
modifier = Modifier
diff --git a/compose/integration-tests/docs-snippets/build.gradle b/compose/integration-tests/docs-snippets/build.gradle
index b2e864f..20bc90f 100644
--- a/compose/integration-tests/docs-snippets/build.gradle
+++ b/compose/integration-tests/docs-snippets/build.gradle
@@ -42,14 +42,8 @@
implementation(project(":compose:ui:ui-viewbinding"))
implementation(project(":navigation:navigation-compose"))
implementation("androidx.activity:activity-compose:1.3.1")
- implementation(project(":lifecycle:lifecycle-viewmodel-compose"))
- // old version of common-java8 conflicts with newer version, because both have
- // DefaultLifecycleEventObserver.
- // Outside of androidx this is resolved via constraint added to lifecycle-common,
- // but it doesn't work in androidx.
- // See aosp/1804059
- implementation("androidx.lifecycle:lifecycle-common-java8:2.5.1")
- implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.5.1")
+ implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
+ implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1")
implementation(project(":paging:paging-compose"))
implementation(libs.kotlinStdlib)
diff --git a/compose/material/material/api/current.txt b/compose/material/material/api/current.txt
index 4eea647..c9c5c53 100644
--- a/compose/material/material/api/current.txt
+++ b/compose/material/material/api/current.txt
@@ -7,7 +7,8 @@
}
public final class AndroidMenu_androidKt {
- method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @Deprecated @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<? extends kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,? extends kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
}
diff --git a/compose/material/material/api/public_plus_experimental_current.txt b/compose/material/material/api/public_plus_experimental_current.txt
index d9fe905..feb60c4b 100644
--- a/compose/material/material/api/public_plus_experimental_current.txt
+++ b/compose/material/material/api/public_plus_experimental_current.txt
@@ -7,7 +7,8 @@
}
public final class AndroidMenu_androidKt {
- method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @Deprecated @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<? extends kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,? extends kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
}
@@ -426,7 +427,7 @@
}
@androidx.compose.material.ExperimentalMaterialApi @kotlin.jvm.JvmDefaultWithCompatibility public interface ExposedDropdownMenuBoxScope {
- method @androidx.compose.runtime.Composable public default void ExposedDropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public default void ExposedDropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.ScrollState scrollState, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
method public androidx.compose.ui.Modifier exposedDropdownSize(androidx.compose.ui.Modifier, optional boolean matchTextFieldWidth);
}
diff --git a/compose/material/material/api/restricted_current.txt b/compose/material/material/api/restricted_current.txt
index 4eea647..c9c5c53 100644
--- a/compose/material/material/api/restricted_current.txt
+++ b/compose/material/material/api/restricted_current.txt
@@ -7,7 +7,8 @@
}
public final class AndroidMenu_androidKt {
- method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @Deprecated @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<? extends kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,? extends kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
}
diff --git a/compose/material/material/integration-tests/material-catalog/src/main/java/androidx/compose/material/catalog/library/model/Examples.kt b/compose/material/material/integration-tests/material-catalog/src/main/java/androidx/compose/material/catalog/library/model/Examples.kt
index ccbd6ff..30fa679 100644
--- a/compose/material/material/integration-tests/material-catalog/src/main/java/androidx/compose/material/catalog/library/model/Examples.kt
+++ b/compose/material/material/integration-tests/material-catalog/src/main/java/androidx/compose/material/catalog/library/model/Examples.kt
@@ -50,6 +50,7 @@
import androidx.compose.material.samples.LeadingIconTabs
import androidx.compose.material.samples.LinearProgressIndicatorSample
import androidx.compose.material.samples.MenuSample
+import androidx.compose.material.samples.MenuWithScrollStateSample
import androidx.compose.material.samples.ModalBottomSheetSample
import androidx.compose.material.samples.ModalDrawerSample
import androidx.compose.material.samples.NavigationRailBottomAlignSample
@@ -398,6 +399,13 @@
MenuSample()
},
Example(
+ name = ::MenuWithScrollStateSample.name,
+ description = MenusExampleDescription,
+ sourceUrl = MenusExampleSourceUrl
+ ) {
+ MenuWithScrollStateSample()
+ },
+ Example(
name = ::ExposedDropdownMenuSample.name,
description = MenusExampleDescription,
sourceUrl = MenusExampleSourceUrl
@@ -703,14 +711,18 @@
description = TextFieldsExampleDescription,
sourceUrl = TextFieldsExampleSourceUrl
) {
- TextArea()
+ TextArea()
}
).map {
// By default text field samples are minimal and don't have a `width` modifier to restrict the
// width. As a result, they grow horizontally if enough text is typed. To prevent this behavior
// in Catalog app the code below restricts the width of every text field sample
it.copy(content = {
- Box(Modifier.wrapContentWidth().width(280.dp)) { it.content() }
+ Box(
+ Modifier
+ .wrapContentWidth()
+ .width(280.dp)
+ ) { it.content() }
})
}
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/MenuSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/MenuSamples.kt
index bf6f420..f9c6a77 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/MenuSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/MenuSamples.kt
@@ -20,6 +20,7 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.Divider
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
@@ -29,6 +30,7 @@
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -41,7 +43,9 @@
fun MenuSample() {
var expanded by remember { mutableStateOf(false) }
- Box(modifier = Modifier.fillMaxSize().wrapContentSize(Alignment.TopStart)) {
+ Box(modifier = Modifier
+ .fillMaxSize()
+ .wrapContentSize(Alignment.TopStart)) {
IconButton(onClick = { expanded = true }) {
Icon(Icons.Default.MoreVert, contentDescription = "Localized description")
}
@@ -61,4 +65,37 @@
}
}
}
-}
\ No newline at end of file
+}
+
+@Sampled
+@Composable
+fun MenuWithScrollStateSample() {
+ var expanded by remember { mutableStateOf(false) }
+ val scrollState = rememberScrollState()
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .wrapContentSize(Alignment.TopStart)
+ ) {
+ IconButton(onClick = { expanded = true }) {
+ Icon(Icons.Default.MoreVert, contentDescription = "Localized description")
+ }
+ DropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false },
+ scrollState = scrollState
+ ) {
+ repeat(30) {
+ DropdownMenuItem(onClick = { /* Handle item! */ }) {
+ Text("Item ${it + 1}")
+ }
+ }
+ }
+ LaunchedEffect(expanded) {
+ if (expanded) {
+ // Scroll to show the bottom menu items.
+ scrollState.scrollTo(scrollState.maxValue)
+ }
+ }
+ }
+}
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AlertDialogTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AlertDialogTest.kt
index e961cd7..147f199 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AlertDialogTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AlertDialogTest.kt
@@ -28,9 +28,13 @@
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.getUnclippedBoundsInRoot
import androidx.compose.ui.test.isDialog
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
@@ -117,4 +121,45 @@
assertThat(dialogWidth).isLessThan(screenWidth)
}
}
+
+ @Test
+ fun alertDialog_positioningActionsWithLongText() {
+ rule.setMaterialContent {
+ AlertDialog(
+ onDismissRequest = {},
+ title = { Text(text = "Title") },
+ text = { Text("Text") },
+ confirmButton = {
+ TextButton(
+ onClick = { /* doSomething() */ },
+ Modifier
+ .testTag(ConfirmButtonTestTag)
+ .semantics(mergeDescendants = true) {}
+ ) {
+ Text("Confirm with a long text")
+ }
+ },
+ dismissButton = {
+ TextButton(
+ onClick = { /* doSomething() */ },
+ Modifier
+ .testTag(DismissButtonTestTag)
+ .semantics(mergeDescendants = true) {}
+ ) {
+ Text("Dismiss with a long text")
+ }
+ }
+ )
+ }
+
+ val confirmBtBounds = rule.onNodeWithTag(ConfirmButtonTestTag).getUnclippedBoundsInRoot()
+ val dismissBtBounds = rule.onNodeWithTag(DismissButtonTestTag).getUnclippedBoundsInRoot()
+
+ assert(dismissBtBounds.top > confirmBtBounds.bottom) {
+ "dismiss action should appear below the confirm action"
+ }
+ }
}
+
+private const val ConfirmButtonTestTag = "confirmButton"
+private const val DismissButtonTestTag = "dismissButton"
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ExposedDropdownMenuTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ExposedDropdownMenuTest.kt
index 17c6cc8..23300d9 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ExposedDropdownMenuTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ExposedDropdownMenuTest.kt
@@ -22,7 +22,9 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -35,9 +37,11 @@
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsFocused
+import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertTextContains
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
@@ -336,6 +340,47 @@
// Should not have crashed.
}
+ @Test
+ fun withScrolledContent() {
+ rule.setMaterialContent {
+ Box(Modifier.fillMaxSize()) {
+ ExposedDropdownMenuBox(
+ modifier = Modifier.align(Alignment.Center),
+ expanded = true,
+ onExpandedChange = { }
+ ) {
+ val scrollState = rememberScrollState()
+ TextField(
+ value = "",
+ onValueChange = { },
+ label = { Text("Label") },
+ )
+ ExposedDropdownMenu(
+ expanded = true,
+ onDismissRequest = { },
+ scrollState = scrollState
+ ) {
+ repeat(100) {
+ Box(
+ Modifier
+ .testTag("MenuContent ${it + 1}")
+ .size(with(LocalDensity.current) { 70.toDp() })
+ )
+ }
+ }
+ LaunchedEffect(Unit) {
+ scrollState.scrollTo(scrollState.maxValue)
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+
+ rule.onNodeWithTag("MenuContent 1").assertIsNotDisplayed()
+ rule.onNodeWithTag("MenuContent 100").assertIsDisplayed()
+ }
+
@Composable
fun ExposedDropdownMenuForTest(
expanded: Boolean,
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/MenuTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/MenuTest.kt
index ace0f2a..8ad3242 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/MenuTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/MenuTest.kt
@@ -21,6 +21,8 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@@ -29,6 +31,8 @@
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.hasAnyDescendant
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.isPopup
@@ -128,6 +132,42 @@
}
@Test
+ fun menu_scrolledContent() {
+ rule.setContent {
+ with(LocalDensity.current) {
+ Box(
+ Modifier
+ .requiredSize(20.toDp())
+ .background(color = Color.Blue)
+ ) {
+ val scrollState = rememberScrollState()
+ DropdownMenu(
+ expanded = true,
+ onDismissRequest = {},
+ scrollState = scrollState
+ ) {
+ repeat(100) {
+ Box(
+ Modifier
+ .testTag("MenuContent ${it + 1}")
+ .size(70.toDp())
+ )
+ }
+ }
+ LaunchedEffect(Unit) {
+ scrollState.scrollTo(scrollState.maxValue)
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+
+ rule.onNodeWithTag("MenuContent 1").assertIsNotDisplayed()
+ rule.onNodeWithTag("MenuContent 100").assertIsDisplayed()
+ }
+
+ @Test
fun menu_positioning_bottomEnd() {
val screenWidth = 500
val screenHeight = 1000
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/TextTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/TextTest.kt
index 3c7b4da..5fea47d 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/TextTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/TextTest.kt
@@ -133,7 +133,6 @@
@Test
fun settingParametersExplicitly() {
- var textColor: Color? = null
var textAlign: TextAlign? = null
var fontSize: TextUnit? = null
var fontStyle: FontStyle? = null
@@ -155,7 +154,6 @@
fontStyle = expectedFontStyle,
letterSpacing = expectedLetterSpacing,
onTextLayout = {
- textColor = it.layoutInput.style.color
textAlign = it.layoutInput.style.textAlign
fontSize = it.layoutInput.style.fontSize
fontStyle = it.layoutInput.style.fontStyle
@@ -168,7 +166,6 @@
rule.runOnIdle {
// explicit parameters should override values from the style.
- Truth.assertThat(textColor).isEqualTo(expectedColor)
Truth.assertThat(textAlign).isEqualTo(expectedTextAlign)
Truth.assertThat(fontSize).isEqualTo(expectedFontSize)
Truth.assertThat(fontStyle).isEqualTo(expectedFontStyle)
@@ -179,7 +176,6 @@
// Not really an expected use-case, but we should ensure the behavior here is consistent.
@Test
fun settingColorAndTextStyle() {
- var textColor: Color? = null
var textAlign: TextAlign? = null
var fontSize: TextUnit? = null
var fontStyle: FontStyle? = null
@@ -202,7 +198,6 @@
letterSpacing = expectedLetterSpacing,
style = ExpectedTextStyle,
onTextLayout = {
- textColor = it.layoutInput.style.color
textAlign = it.layoutInput.style.textAlign
fontSize = it.layoutInput.style.fontSize
fontStyle = it.layoutInput.style.fontStyle
@@ -215,7 +210,6 @@
rule.runOnIdle {
// explicit parameters should override values from the style.
- Truth.assertThat(textColor).isEqualTo(expectedColor)
Truth.assertThat(textAlign).isEqualTo(expectedTextAlign)
Truth.assertThat(fontSize).isEqualTo(expectedFontSize)
Truth.assertThat(fontStyle).isEqualTo(expectedFontStyle)
diff --git a/compose/material/material/src/androidMain/kotlin/androidx/compose/material/AndroidMenu.android.kt b/compose/material/material/src/androidMain/kotlin/androidx/compose/material/AndroidMenu.android.kt
index 94c0373..e1bd14d 100644
--- a/compose/material/material/src/androidMain/kotlin/androidx/compose/material/AndroidMenu.android.kt
+++ b/compose/material/material/src/androidMain/kotlin/androidx/compose/material/AndroidMenu.android.kt
@@ -17,11 +17,13 @@
package androidx.compose.material
import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -70,6 +72,15 @@
* tapping outside the menu's bounds
* @param offset [DpOffset] to be added to the position of the menu
*/
+@Deprecated(
+ level = DeprecationLevel.HIDDEN,
+ replaceWith = ReplaceWith(
+ expression = "DropdownMenu(expanded,onDismissRequest, modifier, offset, " +
+ "rememberScrollState(), properties, content)",
+ "androidx.compose.foundation.rememberScrollState"
+ ),
+ message = "Replaced by a DropdownMenu function with a ScrollState parameter"
+)
@Composable
fun DropdownMenu(
expanded: Boolean,
@@ -78,6 +89,69 @@
offset: DpOffset = DpOffset(0.dp, 0.dp),
properties: PopupProperties = PopupProperties(focusable = true),
content: @Composable ColumnScope.() -> Unit
+) = DropdownMenu(
+ expanded = expanded,
+ onDismissRequest = onDismissRequest,
+ modifier = modifier,
+ offset = offset,
+ scrollState = rememberScrollState(),
+ properties = properties,
+ content = content
+)
+
+/**
+ * <a href="https://material.io/components/menus#dropdown-menu" class="external" target="_blank">Material Design dropdown menu</a>.
+ *
+ * A dropdown menu is a compact way of displaying multiple choices. It appears upon interaction with
+ * an element (such as an icon or button) or when users perform a specific action.
+ *
+ * 
+ *
+ * A [DropdownMenu] behaves similarly to a [Popup], and will use the position of the parent layout
+ * to position itself on screen. Commonly a [DropdownMenu] will be placed in a [Box] with a sibling
+ * that will be used as the 'anchor'. Note that a [DropdownMenu] by itself will not take up any
+ * space in a layout, as the menu is displayed in a separate window, on top of other content.
+ *
+ * The [content] of a [DropdownMenu] will typically be [DropdownMenuItem]s, as well as custom
+ * content. Using [DropdownMenuItem]s will result in a menu that matches the Material
+ * specification for menus. Also note that the [content] is placed inside a scrollable [Column],
+ * so using a [LazyColumn] as the root layout inside [content] is unsupported.
+ *
+ * [onDismissRequest] will be called when the menu should close - for example when there is a
+ * tap outside the menu, or when the back key is pressed.
+ *
+ * [DropdownMenu] changes its positioning depending on the available space, always trying to be
+ * fully visible. It will try to expand horizontally, depending on layout direction, to the end of
+ * its parent, then to the start of its parent, and then screen end-aligned. Vertically, it will
+ * try to expand to the bottom of its parent, then from the top of its parent, and then screen
+ * top-aligned. An [offset] can be provided to adjust the positioning of the menu for cases when
+ * the layout bounds of its parent do not coincide with its visual bounds. Note the offset will
+ * be applied in the direction in which the menu will decide to expand.
+ *
+ * Example usage:
+ * @sample androidx.compose.material.samples.MenuSample
+ *
+ * Example usage with a [ScrollState] to control the menu items scroll position:
+ * @sample androidx.compose.material.samples.MenuWithScrollStateSample
+ *
+ * @param expanded whether the menu is expanded or not
+ * @param onDismissRequest called when the user requests to dismiss the menu, such as by tapping
+ * outside the menu's bounds
+ * @param modifier [Modifier] to be applied to the menu's content
+ * @param offset [DpOffset] to be added to the position of the menu
+ * @param scrollState a [ScrollState] to used by the menu's content for items vertical scrolling
+ * @param properties [PopupProperties] for further customization of this popup's behavior
+ * @param content the content of this dropdown menu, typically a [DropdownMenuItem]
+ */
+@Composable
+fun DropdownMenu(
+ expanded: Boolean,
+ onDismissRequest: () -> Unit,
+ modifier: Modifier = Modifier,
+ offset: DpOffset = DpOffset(0.dp, 0.dp),
+ scrollState: ScrollState = rememberScrollState(),
+ properties: PopupProperties = PopupProperties(focusable = true),
+ content: @Composable ColumnScope.() -> Unit
) {
val expandedStates = remember { MutableTransitionState(false) }
expandedStates.targetState = expanded
@@ -100,6 +174,7 @@
DropdownMenuContent(
expandedStates = expandedStates,
transformOriginState = transformOriginState,
+ scrollState = scrollState,
modifier = modifier,
content = content
)
diff --git a/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.kt b/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.kt
index 4a46e56..660f1c2 100644
--- a/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.kt
+++ b/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.kt
@@ -21,6 +21,7 @@
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.waitForUpOrCancellation
@@ -30,6 +31,7 @@
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.internal.ExposedDropdownMenuPopup
@@ -223,6 +225,7 @@
* @param onDismissRequest Called when the user requests to dismiss the menu, such as by
* tapping outside the menu's bounds
* @param modifier The modifier to apply to this layout
+ * @param scrollState a [ScrollState] to used by the menu's content for items vertical scrolling
* @param content The content of the [ExposedDropdownMenu]
*/
@Composable
@@ -230,6 +233,7 @@
expanded: Boolean,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
+ scrollState: ScrollState = rememberScrollState(),
content: @Composable ColumnScope.() -> Unit
) {
// TODO(b/202810604): use DropdownMenu when PopupProperties constructor is stable
@@ -261,6 +265,7 @@
DropdownMenuContent(
expandedStates = expandedStates,
transformOriginState = transformOriginState,
+ scrollState = scrollState,
modifier = modifier.exposedDropdownSize(),
content = content
)
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AlertDialog.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AlertDialog.kt
index 0c41e95..a469837 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AlertDialog.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AlertDialog.kt
@@ -214,7 +214,8 @@
if (sequences.isNotEmpty()) {
crossAxisSpace += crossAxisSpacing.roundToPx()
}
- sequences += currentSequence.toList()
+ // Ensures that confirming actions appear above dismissive actions.
+ sequences.add(0, currentSequence.toList())
crossAxisSizes += currentCrossAxisSize
crossAxisPositions += crossAxisSpace
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Menu.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Menu.kt
index 3e80837..9d3dadbd 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Menu.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Menu.kt
@@ -21,8 +21,9 @@
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
-import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.IntrinsicSize
@@ -33,14 +34,13 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.getValue
import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -57,11 +57,11 @@
import kotlin.math.max
import kotlin.math.min
-@Suppress("ModifierParameter")
@Composable
internal fun DropdownMenuContent(
expandedStates: MutableTransitionState<Boolean>,
transformOriginState: MutableState<TransformOrigin>,
+ scrollState: ScrollState,
modifier: Modifier = Modifier,
content: @Composable ColumnScope.() -> Unit
) {
@@ -126,7 +126,7 @@
modifier = modifier
.padding(vertical = DropdownMenuVerticalPadding)
.width(IntrinsicSize.Max)
- .verticalScroll(rememberScrollState()),
+ .verticalScroll(scrollState),
content = content
)
}
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
index 30641c4..316474c 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
@@ -59,6 +59,7 @@
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -190,8 +191,8 @@
scale(valueRange.start, valueRange.endInclusive, userValue, minPx, maxPx)
val scope = rememberCoroutineScope()
- val rawOffset = remember { mutableStateOf(scaleToOffset(value)) }
- val pressOffset = remember { mutableStateOf(0f) }
+ val rawOffset = remember { mutableFloatStateOf(scaleToOffset(value)) }
+ val pressOffset = remember { mutableFloatStateOf(0f) }
val draggableState = remember(minPx, maxPx, valueRange) {
SliderDraggableState {
@@ -328,8 +329,8 @@
fun scaleToOffset(userValue: Float) =
scale(valueRange.start, valueRange.endInclusive, userValue, minPx, maxPx)
- val rawOffsetStart = remember { mutableStateOf(scaleToOffset(value.start)) }
- val rawOffsetEnd = remember { mutableStateOf(scaleToOffset(value.endInclusive)) }
+ val rawOffsetStart = remember { mutableFloatStateOf(scaleToOffset(value.start)) }
+ val rawOffsetEnd = remember { mutableFloatStateOf(scaleToOffset(value.endInclusive)) }
CorrectValueSideEffect(
::scaleToOffset,
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Swipeable.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Swipeable.kt
index dbd9be9..b30abb2 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Swipeable.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Swipeable.kt
@@ -34,6 +34,7 @@
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.Saver
@@ -110,12 +111,12 @@
val overflow: State<Float> get() = overflowState
// Use `Float.NaN` as a placeholder while the state is uninitialised.
- private val offsetState = mutableStateOf(0f)
- private val overflowState = mutableStateOf(0f)
+ private val offsetState = mutableFloatStateOf(0f)
+ private val overflowState = mutableFloatStateOf(0f)
// the source of truth for the "real"(non ui) position
// basically position in bounds + overflow
- private val absoluteOffset = mutableStateOf(0f)
+ private val absoluteOffset = mutableFloatStateOf(0f)
// current animation target, if animating, otherwise null
private val animationTarget = mutableStateOf<Float?>(null)
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt
index 618aff5d..d2992f3 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt
@@ -24,7 +24,7 @@
import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.takeOrElse
+import androidx.compose.ui.graphics.isSpecified
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.Paragraph
import androidx.compose.ui.text.TextLayoutResult
@@ -109,18 +109,37 @@
onTextLayout: ((TextLayoutResult) -> Unit)? = null,
style: TextStyle = LocalTextStyle.current
) {
-
- val textColor = color.takeOrElse {
- style.color.takeOrElse {
- LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
- }
+ // TL:DR: profile before you change any line of code in this method
+ //
+ // The call to LocalContentAlpha.current looks like it can be avoided by only calling it in the
+ // last else block but, in 1.5, this causes a control flow group to be created because it would
+ // be a conditional call to a composable function. The call is currently made unconditionally
+ // since the call to LocalContentAlpha.current does not create a group (it is a read-only
+ // composable) and looking up the value in the composition locals map is currently faster than
+ // creating a group to avoid it.
+ //
+ // Similar notes regarding lambda allocations. It appears there's a path to optimize for
+ // zero-allocations in the style-provided color route, but this either introduces a group or a
+ // box depending on how it's coded. It's also possible that allocating a final ColorProducer
+ // subclass with no capture may be a successful optimization, but it appeared slower in initial
+ // profiling.
+ //
+ // If changing ANY LINE OF CODE, please confirm that it's faster or the same speed using
+ // profilers and benchmarks.
+ val localContentColor = LocalContentColor.current
+ val localContentAlpha = LocalContentAlpha.current
+ val overrideColorOrUnspecified: Color = if (color.isSpecified) {
+ color
+ } else if (style.color.isSpecified) {
+ style.color
+ } else {
+ localContentColor.copy(localContentAlpha)
}
BasicText(
text = text,
modifier = modifier,
style = style.merge(
- color = textColor,
fontSize = fontSize,
fontWeight = fontWeight,
textAlign = textAlign,
@@ -134,7 +153,8 @@
overflow = overflow,
softWrap = softWrap,
maxLines = maxLines,
- minLines = minLines
+ minLines = minLines,
+ color = { overrideColorOrUnspecified }
)
}
@@ -257,17 +277,37 @@
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
) {
- val textColor = color.takeOrElse {
- style.color.takeOrElse {
- LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
- }
+ // TL:DR: profile before you change any line of code in this method
+ //
+ // The call to LocalContentAlpha.current looks like it can be avoided by only calling it in the
+ // last else block but, in 1.5, this causes a control flow group to be created because it would
+ // be a conditional call to a composable function. The call is currently made unconditionally
+ // since the call to LocalContentAlpha.current does not create a group (it is a read-only
+ // composable) and looking up the value in the composition locals map is currently faster than
+ // creating a group to avoid it.
+ //
+ // Similar notes regarding lambda allocations. It appears there's a path to optimize for
+ // zero-allocations in the style-provided color route, but this either introduces a group or a
+ // box depending on how it's coded. It's also possible that allocating a final ColorProducer
+ // subclass with no capture may be a successful optimization, but it appeared slower in initial
+ // profiling.
+ //
+ // If changing ANY LINE OF CODE, please confirm that it's faster or the same speed using
+ // profilers and benchmarks.
+ val localContentColor = LocalContentColor.current
+ val localContentAlpha = LocalContentAlpha.current
+ val overrideColorOrUnspecified = if (color.isSpecified) {
+ color
+ } else if (style.color.isSpecified) {
+ style.color
+ } else {
+ localContentColor.copy(localContentAlpha)
}
BasicText(
text = text,
modifier = modifier,
style = style.merge(
- color = textColor,
fontSize = fontSize,
fontWeight = fontWeight,
textAlign = textAlign,
@@ -282,7 +322,8 @@
softWrap = softWrap,
maxLines = maxLines,
minLines = minLines,
- inlineContent = inlineContent
+ inlineContent = inlineContent,
+ color = { overrideColorOrUnspecified }
)
}
diff --git a/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopMenu.desktop.kt b/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopMenu.desktop.kt
index 4da69f2..c197e65 100644
--- a/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopMenu.desktop.kt
+++ b/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopMenu.desktop.kt
@@ -21,6 +21,8 @@
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.mutableStateOf
@@ -74,7 +76,15 @@
* @param offset [DpOffset] to be added to the position of the menu
* @param content content lambda
*/
-@Suppress("ModifierParameter")
+@Deprecated(
+ level = DeprecationLevel.HIDDEN,
+ replaceWith = ReplaceWith(
+ expression = "DropdownMenu(expanded,onDismissRequest, focusable, modifier, offset, " +
+ "rememberScrollState(), content)",
+ "androidx.compose.foundation.rememberScrollState"
+ ),
+ message = "Replaced by a DropdownMenu function with a ScrollState parameter"
+)
@Composable
fun DropdownMenu(
expanded: Boolean,
@@ -83,6 +93,64 @@
modifier: Modifier = Modifier,
offset: DpOffset = DpOffset(0.dp, 0.dp),
content: @Composable ColumnScope.() -> Unit
+) = DropdownMenu(
+ expanded = expanded,
+ onDismissRequest = onDismissRequest,
+ focusable = focusable,
+ modifier = modifier,
+ offset = offset,
+ scrollState = rememberScrollState(),
+ content = content
+)
+
+/**
+ * A Material Design [dropdown menu](https://material.io/components/menus#dropdown-menu).
+ *
+ * A [DropdownMenu] behaves similarly to a [Popup], and will use the position of the parent layout
+ * to position itself on screen. Commonly a [DropdownMenu] will be placed in a [Box] with a sibling
+ * that will be used as the 'anchor'. Note that a [DropdownMenu] by itself will not take up any
+ * space in a layout, as the menu is displayed in a separate window, on top of other content.
+ *
+ * The [content] of a [DropdownMenu] will typically be [DropdownMenuItem]s, as well as custom
+ * content. Using [DropdownMenuItem]s will result in a menu that matches the Material
+ * specification for menus. Also note that the [content] is placed inside a scrollable [Column],
+ * so using a [LazyColumn] as the root layout inside [content] is unsupported.
+ *
+ * [onDismissRequest] will be called when the menu should close - for example when there is a
+ * tap outside the menu, or when the back key is pressed.
+ *
+ * [DropdownMenu] changes its positioning depending on the available space, always trying to be
+ * fully visible. It will try to expand horizontally, depending on layout direction, to the end of
+ * its parent, then to the start of its parent, and then screen end-aligned. Vertically, it will
+ * try to expand to the bottom of its parent, then from the top of its parent, and then screen
+ * top-aligned. An [offset] can be provided to adjust the positioning of the menu for cases when
+ * the layout bounds of its parent do not coincide with its visual bounds. Note the offset will
+ * be applied in the direction in which the menu will decide to expand.
+ *
+ * Example usage:
+ * @sample androidx.compose.material.samples.MenuSample
+ *
+ * Example usage with a [ScrollState] to control the menu items scroll position:
+ * @sample androidx.compose.material.samples.MenuWithScrollStateSample
+ *
+ * @param expanded Whether the menu is currently open and visible to the user
+ * @param onDismissRequest Called when the user requests to dismiss the menu, such as by
+ * tapping outside the menu's bounds
+ * @param focusable Whether the dropdown can capture focus
+ * @param modifier [Modifier] to be applied to the menu's content
+ * @param offset [DpOffset] to be added to the position of the menu
+ * @param scrollState a [ScrollState] to used by the menu's content for items vertical scrolling
+ * @param content the content of this dropdown menu, typically a [DropdownMenuItem]
+ */
+@Composable
+fun DropdownMenu(
+ expanded: Boolean,
+ onDismissRequest: () -> Unit,
+ focusable: Boolean = true,
+ modifier: Modifier = Modifier,
+ offset: DpOffset = DpOffset(0.dp, 0.dp),
+ scrollState: ScrollState = rememberScrollState(),
+ content: @Composable ColumnScope.() -> Unit
) {
val expandedStates = remember { MutableTransitionState(false) }
expandedStates.targetState = expanded
@@ -110,6 +178,7 @@
expandedStates = expandedStates,
transformOriginState = transformOriginState,
modifier = modifier,
+ scrollState = scrollState,
content = content
)
}
@@ -164,7 +233,15 @@
* @param onDismissRequest Called when the user requests to dismiss the menu, such as by
* tapping outside the menu's bounds
*/
-@Suppress("ModifierParameter")
+@Deprecated(
+ level = DeprecationLevel.HIDDEN,
+ replaceWith = ReplaceWith(
+ expression = "CursorDropdownMenu(expanded,onDismissRequest, focusable, modifier, " +
+ "rememberScrollState(), content)",
+ "androidx.compose.foundation.rememberScrollState"
+ ),
+ message = "Replaced by a CursorDropdownMenu function with a ScrollState parameter"
+)
@Composable
fun CursorDropdownMenu(
expanded: Boolean,
@@ -172,6 +249,40 @@
focusable: Boolean = true,
modifier: Modifier = Modifier,
content: @Composable ColumnScope.() -> Unit
+) = CursorDropdownMenu(
+ expanded = expanded,
+ onDismissRequest = onDismissRequest,
+ focusable = focusable,
+ modifier = modifier,
+ scrollState = rememberScrollState(),
+ content = content
+)
+
+/**
+ *
+ * A [CursorDropdownMenu] behaves similarly to [Popup] and will use the current position of the mouse
+ * cursor to position itself on screen.
+ *
+ * The [content] of a [CursorDropdownMenu] will typically be [DropdownMenuItem]s, as well as custom
+ * content. Using [DropdownMenuItem]s will result in a menu that matches the Material
+ * specification for menus.
+ *
+ * @param expanded Whether the menu is currently open and visible to the user
+ * @param onDismissRequest Called when the user requests to dismiss the menu, such as by
+ * tapping outside the menu's bounds
+ * @param focusable Whether the dropdown can capture focus
+ * @param modifier [Modifier] to be applied to the menu's content
+ * @param scrollState a [ScrollState] to used by the menu's content for items vertical scrolling
+ * @param content the content of this dropdown menu, typically a [DropdownMenuItem]
+ */
+@Composable
+fun CursorDropdownMenu(
+ expanded: Boolean,
+ onDismissRequest: () -> Unit,
+ focusable: Boolean = true,
+ modifier: Modifier = Modifier,
+ scrollState: ScrollState = rememberScrollState(),
+ content: @Composable ColumnScope.() -> Unit
) {
val expandedStates = remember { MutableTransitionState(false) }
expandedStates.targetState = expanded
@@ -188,6 +299,7 @@
expandedStates = expandedStates,
transformOriginState = transformOriginState,
modifier = modifier,
+ scrollState = scrollState,
content = content
)
}
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index d2b53fb..1f5b379 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -22,7 +22,8 @@
}
public final class AndroidMenu_androidKt {
- method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @Deprecated @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<? extends kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,? extends kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean enabled, optional androidx.compose.material3.MenuItemColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
}
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index c580cf9..b2471cb 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -23,7 +23,8 @@
}
public final class AndroidMenu_androidKt {
- method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @Deprecated @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<? extends kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,? extends kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean enabled, optional androidx.compose.material3.MenuItemColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
}
@@ -511,7 +512,7 @@
}
@androidx.compose.material3.ExperimentalMaterial3Api public interface ExposedDropdownMenuBoxScope {
- method @androidx.compose.runtime.Composable public default void ExposedDropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public default void ExposedDropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.ScrollState scrollState, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
method public androidx.compose.ui.Modifier exposedDropdownSize(androidx.compose.ui.Modifier, optional boolean matchTextFieldWidth);
method public androidx.compose.ui.Modifier menuAnchor(androidx.compose.ui.Modifier);
}
@@ -987,6 +988,7 @@
@androidx.compose.runtime.Stable public final class SliderDefaults {
method @androidx.compose.runtime.Composable public void Thumb(androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled, optional long thumbSize);
method @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.SliderPositions sliderPositions, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.SliderState sliderState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled);
method @androidx.compose.runtime.Composable public androidx.compose.material3.SliderColors colors(optional long thumbColor, optional long activeTrackColor, optional long activeTickColor, optional long inactiveTrackColor, optional long inactiveTickColor, optional long disabledThumbColor, optional long disabledActiveTrackColor, optional long disabledActiveTickColor, optional long disabledInactiveTrackColor, optional long disabledInactiveTickColor);
field public static final androidx.compose.material3.SliderDefaults INSTANCE;
}
@@ -995,7 +997,8 @@
method @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors);
method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource startInteractionSource, optional androidx.compose.foundation.interaction.MutableInteractionSource endInteractionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderPositions,kotlin.Unit> startThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderPositions,kotlin.Unit> endThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderPositions,kotlin.Unit> track, optional int steps);
method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
- method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderPositions,kotlin.Unit> thumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderPositions,kotlin.Unit> track, optional int steps);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional int steps, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> thumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> track, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Slider(androidx.compose.material3.SliderState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> thumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> track);
}
@androidx.compose.runtime.Stable public final class SliderPositions {
@@ -1006,6 +1009,20 @@
property public final float[] tickFractions;
}
+ @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class SliderState {
+ ctor public SliderState(optional float initialValue, optional kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit>? initialOnValueChange, optional int steps, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished);
+ method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnValueChangeFinished();
+ method public int getSteps();
+ method public float getValue();
+ method public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> getValueRange();
+ method public void setOnValueChangeFinished(kotlin.jvm.functions.Function0<kotlin.Unit>?);
+ method public void setValue(float);
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished;
+ property public final int steps;
+ property public final float value;
+ property public final kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange;
+ }
+
@androidx.compose.runtime.Stable public interface SnackbarData {
method public void dismiss();
method public androidx.compose.material3.SnackbarVisuals getVisuals();
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index d2b53fb..1f5b379 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -22,7 +22,8 @@
}
public final class AndroidMenu_androidKt {
- method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @Deprecated @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<? extends kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,? extends kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean enabled, optional androidx.compose.material3.MenuItemColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
}
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
index e5369cc..1d6480b 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
@@ -81,6 +81,7 @@
import androidx.compose.material3.samples.LeadingIconTabs
import androidx.compose.material3.samples.LinearProgressIndicatorSample
import androidx.compose.material3.samples.MenuSample
+import androidx.compose.material3.samples.MenuWithScrollStateSample
import androidx.compose.material3.samples.ModalBottomSheetSample
import androidx.compose.material3.samples.ModalNavigationDrawerSample
import androidx.compose.material3.samples.NavigationBarItemWithBadge
@@ -618,6 +619,13 @@
MenuSample()
},
Example(
+ name = ::MenuWithScrollStateSample.name,
+ description = MenusExampleDescription,
+ sourceUrl = MenusExampleSourceUrl
+ ) {
+ MenuWithScrollStateSample()
+ },
+ Example(
name = ::ExposedDropdownMenuSample.name,
description = MenusExampleDescription,
sourceUrl = MenusExampleSourceUrl
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt
index 0c5f3c3..b6e3445 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt
@@ -20,6 +20,7 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.outlined.Edit
@@ -32,6 +33,7 @@
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -47,7 +49,11 @@
fun MenuSample() {
var expanded by remember { mutableStateOf(false) }
- Box(modifier = Modifier.fillMaxSize().wrapContentSize(Alignment.TopStart)) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .wrapContentSize(Alignment.TopStart)
+ ) {
IconButton(onClick = { expanded = true }) {
Icon(Icons.Default.MoreVert, contentDescription = "Localized description")
}
@@ -87,3 +93,43 @@
}
}
}
+
+@Preview
+@Sampled
+@Composable
+fun MenuWithScrollStateSample() {
+ var expanded by remember { mutableStateOf(false) }
+ val scrollState = rememberScrollState()
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .wrapContentSize(Alignment.TopStart)
+ ) {
+ IconButton(onClick = { expanded = true }) {
+ Icon(Icons.Default.MoreVert, contentDescription = "Localized description")
+ }
+ DropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false },
+ scrollState = scrollState
+ ) {
+ repeat(30) {
+ DropdownMenuItem(
+ text = { Text("Item ${it + 1}") },
+ onClick = { /* TODO */ },
+ leadingIcon = {
+ Icon(
+ Icons.Outlined.Edit,
+ contentDescription = null
+ )
+ })
+ }
+ }
+ LaunchedEffect(expanded) {
+ if (expanded) {
+ // Scroll to show the bottom menu items.
+ scrollState.scrollTo(scrollState.maxValue)
+ }
+ }
+ }
+}
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SliderSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SliderSamples.kt
index 154bdd2..56e258c 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SliderSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SliderSamples.kt
@@ -28,6 +28,7 @@
import androidx.compose.material3.RangeSlider
import androidx.compose.material3.Slider
import androidx.compose.material3.SliderDefaults
+import androidx.compose.material3.SliderState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -88,7 +89,7 @@
value = sliderPosition,
onValueChange = { sliderPosition = it },
valueRange = 0f..5f,
- steps = 4,
+ steps = 10,
onValueChangeFinished = {
// launch some business logic update with the state you hold
// viewModel.updateSelectedSliderValue(sliderPosition)
@@ -110,20 +111,22 @@
@Sampled
@Composable
fun SliderWithCustomTrackAndThumb() {
- var sliderPosition by remember { mutableStateOf(0f) }
- val interactionSource = MutableInteractionSource()
- val colors = SliderDefaults.colors(thumbColor = Color.Red, activeTrackColor = Color.Red)
- Column {
- Text(text = sliderPosition.toString())
- Slider(
- modifier = Modifier.semantics { contentDescription = "Localized Description" },
- value = sliderPosition,
- onValueChange = { sliderPosition = it },
+ val sliderState = remember {
+ SliderState(
valueRange = 0f..100f,
onValueChangeFinished = {
// launch some business logic update with the state you hold
// viewModel.updateSelectedSliderValue(sliderPosition)
- },
+ }
+ )
+ }
+ val interactionSource = MutableInteractionSource()
+ val colors = SliderDefaults.colors(thumbColor = Color.Red, activeTrackColor = Color.Red)
+ Column {
+ Text(text = sliderState.value.toString())
+ Slider(
+ state = sliderState,
+ modifier = Modifier.semantics { contentDescription = "Localized Description" },
interactionSource = interactionSource,
thumb = {
SliderDefaults.Thumb(
@@ -131,17 +134,16 @@
colors = colors
)
},
- track = { sliderPositions ->
+ track = {
SliderDefaults.Track(
colors = colors,
- sliderPositions = sliderPositions
+ sliderState = sliderState
)
}
)
}
}
-@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Sampled
@Composable
@@ -162,7 +164,6 @@
}
}
-@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Sampled
@Composable
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AlertDialogTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AlertDialogTest.kt
index 83b1f2b..d3ed41d 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AlertDialogTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AlertDialogTest.kt
@@ -418,6 +418,44 @@
}
@Test
+ fun alertDialog_positioningActionsWithLongText() {
+ rule.setMaterialContent(lightColorScheme()) {
+ AlertDialog(
+ onDismissRequest = {},
+ title = { Text(text = "Title") },
+ text = { Text("Text") },
+ confirmButton = {
+ TextButton(
+ onClick = { /* doSomething() */ },
+ Modifier
+ .testTag(ConfirmButtonTestTag)
+ .semantics(mergeDescendants = true) {}
+ ) {
+ Text("Confirm with a long text")
+ }
+ },
+ dismissButton = {
+ TextButton(
+ onClick = { /* doSomething() */ },
+ Modifier
+ .testTag(DismissButtonTestTag)
+ .semantics(mergeDescendants = true) {}
+ ) {
+ Text("Dismiss with a long text")
+ }
+ }
+ )
+ }
+
+ val confirmBtBounds = rule.onNodeWithTag(ConfirmButtonTestTag).getUnclippedBoundsInRoot()
+ val dismissBtBounds = rule.onNodeWithTag(DismissButtonTestTag).getUnclippedBoundsInRoot()
+
+ assert(dismissBtBounds.top > confirmBtBounds.bottom) {
+ "dismiss action should appear below the confirm action"
+ }
+ }
+
+ @Test
fun alertDialog_positioningWithLazyColumnText() {
rule.setMaterialContent(lightColorScheme()) {
AlertDialog(
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DividerScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DividerScreenshotTest.kt
index 7374b08..1f62b814 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DividerScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DividerScreenshotTest.kt
@@ -31,6 +31,7 @@
import androidx.test.filters.MediumTest
import androidx.test.filters.SdkSuppress
import androidx.test.screenshot.AndroidXScreenshotTestRule
+import org.junit.Assume.assumeFalse
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -64,6 +65,8 @@
@Test
fun darkTheme() {
+ assumeFalse("See b/272301182", Build.VERSION.SDK_INT == 33)
+
composeTestRule.setMaterialContent(darkColorScheme()) {
Column(Modifier.testTag(Tag)) {
Spacer(Modifier.size(10.dp))
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
index 66ae652..24bde2e 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
@@ -22,7 +22,9 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -35,9 +37,11 @@
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsFocused
+import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertTextContains
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
@@ -198,11 +202,17 @@
modifier = Modifier.padding(8.dp),
) {
TextField(
- modifier = Modifier.menuAnchor().then(
- if (index == testIndex) Modifier.testTag(TFTag).onSizeChanged {
- textFieldSize = it
- } else { Modifier }
- ),
+ modifier = Modifier
+ .menuAnchor()
+ .then(
+ if (index == testIndex) Modifier
+ .testTag(TFTag)
+ .onSizeChanged {
+ textFieldSize = it
+ } else {
+ Modifier
+ }
+ ),
value = selectedOptionText,
onValueChange = { selectedOptionText = it },
label = { Text("Label") },
@@ -315,7 +325,10 @@
setContent {
Box {
ExposedDropdownMenuBox(expanded = true, onExpandedChange = {}) {
- Box(Modifier.menuAnchor().size(20.dp))
+ Box(
+ Modifier
+ .menuAnchor()
+ .size(20.dp))
}
}
}
@@ -334,6 +347,49 @@
// Should not have crashed.
}
+ @OptIn(ExperimentalMaterial3Api::class)
+ @Test
+ fun withScrolledContent() {
+ rule.setMaterialContent(lightColorScheme()) {
+ Box(Modifier.fillMaxSize()) {
+ ExposedDropdownMenuBox(
+ modifier = Modifier.align(Alignment.Center),
+ expanded = true,
+ onExpandedChange = { }
+ ) {
+ val scrollState = rememberScrollState()
+ TextField(
+ modifier = Modifier.menuAnchor(),
+ value = "",
+ onValueChange = { },
+ label = { Text("Label") },
+ )
+ ExposedDropdownMenu(
+ expanded = true,
+ onDismissRequest = { },
+ scrollState = scrollState
+ ) {
+ repeat(100) {
+ Box(
+ Modifier
+ .testTag("MenuContent ${it + 1}")
+ .size(with(LocalDensity.current) { 70.toDp() })
+ )
+ }
+ }
+ LaunchedEffect(Unit) {
+ scrollState.scrollTo(scrollState.maxValue)
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+
+ rule.onNodeWithTag("MenuContent 1").assertIsNotDisplayed()
+ rule.onNodeWithTag("MenuContent 100").assertIsDisplayed()
+ }
+
@Composable
fun ExposedDropdownMenuForTest(
expanded: Boolean,
@@ -349,7 +405,9 @@
onExpandedChange = { onExpandChange(!expanded) }
) {
TextField(
- modifier = Modifier.menuAnchor().testTag(TFTag)
+ modifier = Modifier
+ .menuAnchor()
+ .testTag(TFTag)
.onGloballyPositioned {
onTextFieldBoundsChanged?.invoke(it.boundsInRoot())
},
@@ -368,9 +426,11 @@
colors = ExposedDropdownMenuDefaults.textFieldColors()
)
ExposedDropdownMenu(
- modifier = Modifier.testTag(EDMTag).onGloballyPositioned {
- onMenuBoundsChanged?.invoke(it.boundsInRoot())
- },
+ modifier = Modifier
+ .testTag(EDMTag)
+ .onGloballyPositioned {
+ onMenuBoundsChanged?.invoke(it.boundsInRoot())
+ },
expanded = expanded,
onDismissRequest = { onExpandChange(false) }
) {
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt
index 2810336..74c0d5c 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt
@@ -20,6 +20,7 @@
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material.icons.outlined.Email
@@ -100,10 +101,14 @@
@Composable
private fun TestMenu(enabledItems: Boolean) {
- Box(Modifier.testTag(testTag).padding(20.dp), contentAlignment = Alignment.Center) {
+ Box(
+ Modifier
+ .testTag(testTag)
+ .padding(20.dp), contentAlignment = Alignment.Center) {
DropdownMenuContent(
expandedStates = MutableTransitionState(initialState = true),
- transformOriginState = remember { mutableStateOf(TransformOrigin.Center) }
+ transformOriginState = remember { mutableStateOf(TransformOrigin.Center) },
+ scrollState = rememberScrollState()
) {
DropdownMenuItem(
text = { Text("Edit") },
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuTest.kt
index 124eac9..26f91f1 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuTest.kt
@@ -21,6 +21,8 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@@ -28,7 +30,8 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.hasAnyDescendant
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.isPopup
@@ -51,7 +54,6 @@
@MediumTest
@RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalTestApi::class)
class MenuTest {
@get:Rule
val rule = createComposeRule()
@@ -61,7 +63,11 @@
var expanded by mutableStateOf(false)
rule.setContent {
- Box(Modifier.requiredSize(20.dp).background(color = Color.Blue)) {
+ Box(
+ Modifier
+ .requiredSize(20.dp)
+ .background(color = Color.Blue)
+ ) {
DropdownMenu(
expanded = expanded,
onDismissRequest = {}
@@ -103,13 +109,25 @@
fun menu_hasExpectedSize() {
rule.setContent {
with(LocalDensity.current) {
- Box(Modifier.requiredSize(20.toDp()).background(color = Color.Blue)) {
+ Box(
+ Modifier
+ .requiredSize(20.toDp())
+ .background(color = Color.Blue)
+ ) {
DropdownMenu(
expanded = true,
onDismissRequest = {}
) {
- Box(Modifier.testTag("MenuContent1").size(70.toDp()))
- Box(Modifier.testTag("MenuContent2").size(130.toDp()))
+ Box(
+ Modifier
+ .testTag("MenuContent1")
+ .size(70.toDp())
+ )
+ Box(
+ Modifier
+ .testTag("MenuContent2")
+ .size(130.toDp())
+ )
}
}
}
@@ -129,6 +147,42 @@
}
@Test
+ fun menu_scrolledContent() {
+ rule.setContent {
+ with(LocalDensity.current) {
+ Box(
+ Modifier
+ .requiredSize(20.toDp())
+ .background(color = Color.Blue)
+ ) {
+ val scrollState = rememberScrollState()
+ DropdownMenu(
+ expanded = true,
+ onDismissRequest = {},
+ scrollState = scrollState
+ ) {
+ repeat(100) {
+ Box(
+ Modifier
+ .testTag("MenuContent ${it + 1}")
+ .size(70.toDp())
+ )
+ }
+ }
+ LaunchedEffect(Unit) {
+ scrollState.scrollTo(scrollState.maxValue)
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+
+ rule.onNodeWithTag("MenuContent 1").assertIsNotDisplayed()
+ rule.onNodeWithTag("MenuContent 100").assertIsDisplayed()
+ }
+
+ @Test
fun menu_positioning_bottomEnd() {
val screenWidth = 500
val screenHeight = 1000
@@ -349,7 +403,9 @@
DropdownMenuItem(
text = { Box(Modifier.requiredSize(40.dp)) },
onClick,
- modifier = Modifier.testTag("MenuItem").clickable(onClick = onClick),
+ modifier = Modifier
+ .testTag("MenuItem")
+ .clickable(onClick = onClick),
)
}
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarScreenshotTest.kt
index 3693245..3c05d96 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarScreenshotTest.kt
@@ -21,6 +21,7 @@
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.height
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.runtime.Composable
@@ -33,6 +34,7 @@
import androidx.compose.ui.test.captureToImage
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.filters.SdkSuppress
@@ -112,6 +114,25 @@
}
@Test
+ fun lightTheme_customHeight() {
+ val interactionSource = MutableInteractionSource()
+
+ var scope: CoroutineScope? = null
+
+ composeTestRule.setMaterialContent(lightColorScheme()) {
+ scope = rememberCoroutineScope()
+ DefaultNavigationBar(interactionSource, Modifier.height(64.dp))
+ }
+
+ assertNavigationBarMatches(
+ scope = scope!!,
+ interactionSource = interactionSource,
+ interaction = null,
+ goldenIdentifier = "navigationBar_lightTheme_customHeight"
+ )
+ }
+
+ @Test
fun darkTheme_defaultColors() {
val interactionSource = MutableInteractionSource()
@@ -211,14 +232,16 @@
*
* @param interactionSource the [MutableInteractionSource] for the first [NavigationBarItem], to
* control its visual state.
+ * @param modifier the [Modifier] applied to the navigation bar
* @param setUnselectedItemsAsDisabled when true, marks unselected items as disabled
*/
@Composable
private fun DefaultNavigationBar(
interactionSource: MutableInteractionSource,
+ modifier: Modifier = Modifier,
setUnselectedItemsAsDisabled: Boolean = false,
) {
- Box(Modifier.semantics(mergeDescendants = true) {}.testTag(Tag)) {
+ Box(modifier.semantics(mergeDescendants = true) {}.testTag(Tag)) {
NavigationBar {
NavigationBarItem(
icon = { Icon(Icons.Filled.Favorite, null) },
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarTest.kt
index e9a0168..2f6c21f 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarTest.kt
@@ -19,6 +19,7 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material3.tokens.NavigationBarTokens
@@ -266,42 +267,43 @@
@Test
fun navigationBarItemContent_withLabel_sizeAndPosition() {
rule.setMaterialContent(lightColorScheme()) {
- Box {
- NavigationBar {
- NavigationBarItem(
- modifier = Modifier.testTag("item"),
- icon = {
- Icon(Icons.Filled.Favorite, null, Modifier.testTag("icon"))
- },
- label = {
- Text("ItemText")
- },
- selected = true,
- onClick = {}
- )
- }
+ NavigationBar {
+ NavigationBarItem(
+ modifier = Modifier.testTag("item"),
+ icon = {
+ Icon(Icons.Filled.Favorite, null, Modifier.testTag("icon"))
+ },
+ label = {
+ Text("ItemText")
+ },
+ selected = true,
+ onClick = {}
+ )
}
}
val itemBounds = rule.onNodeWithTag("item").getUnclippedBoundsInRoot()
val iconBounds = rule.onNodeWithTag("icon", useUnmergedTree = true)
.getUnclippedBoundsInRoot()
- val textBounds = rule.onNodeWithText("ItemText", useUnmergedTree = true)
- .getUnclippedBoundsInRoot()
- // Distance from the bottom of the item to the text bottom, and from the top of the icon to
- // the top of the item
- val verticalPadding = NavigationBarItemVerticalPadding
-
- val itemBottom = itemBounds.height + itemBounds.top
- // Text bottom should be `verticalPadding` from the bottom of the item
- textBounds.bottom.assertIsEqualTo(itemBottom - verticalPadding)
+ // Distance from the top of the item to the top of the icon for the default height
+ val verticalPadding = 16.dp
rule.onNodeWithTag("icon", useUnmergedTree = true)
- // The icon should be centered in the item
+ // The icon should be horizontally centered in the item
.assertLeftPositionInRootIsEqualTo((itemBounds.width - iconBounds.width) / 2)
// The top of the icon is `verticalPadding` below the top of the item
.assertTopPositionInRootIsEqualTo(itemBounds.top + verticalPadding)
+
+ val iconBottom = iconBounds.top + iconBounds.height
+ // Text should be `IndicatorVerticalPadding + NavigationBarIndicatorToLabelPadding` from the
+ // bottom of the icon
+ rule.onNodeWithText("ItemText", useUnmergedTree = true)
+ .getUnclippedBoundsInRoot()
+ .top
+ .assertIsEqualTo(
+ iconBottom + IndicatorVerticalPadding + NavigationBarIndicatorToLabelPadding
+ )
}
@Test
@@ -367,6 +369,49 @@
}
@Test
+ fun navigationBarItemContent_customHeight_withLabel_sizeAndPosition() {
+ val defaultHeight = NavigationBarTokens.ContainerHeight
+ val customHeight = 64.dp
+
+ rule.setMaterialContent(lightColorScheme()) {
+ NavigationBar(Modifier.height(customHeight)) {
+ NavigationBarItem(
+ modifier = Modifier.testTag("item"),
+ icon = {
+ Icon(Icons.Filled.Favorite, null, Modifier.testTag("icon"))
+ },
+ label = { Text("Label") },
+ selected = true,
+ onClick = {}
+ )
+ }
+ }
+
+ // Vertical padding is removed symmetrically from top and bottom for smaller heights
+ val verticalPadding = 16.dp - (defaultHeight - customHeight) / 2
+
+ val itemBounds = rule.onNodeWithTag("item").getUnclippedBoundsInRoot()
+ val iconBounds = rule.onNodeWithTag("icon", useUnmergedTree = true)
+ .getUnclippedBoundsInRoot()
+
+ rule.onNodeWithTag("icon", useUnmergedTree = true)
+ // The icon should be horizontally centered in the item
+ .assertLeftPositionInRootIsEqualTo((itemBounds.width - iconBounds.width) / 2)
+ // The top of the icon is `verticalPadding` below the top of the item
+ .assertTopPositionInRootIsEqualTo(itemBounds.top + verticalPadding)
+
+ val iconBottom = iconBounds.top + iconBounds.height
+ // Text should be `IndicatorVerticalPadding + NavigationBarIndicatorToLabelPadding` from the
+ // bottom of the item
+ rule.onNodeWithText("Label", useUnmergedTree = true)
+ .getUnclippedBoundsInRoot()
+ .top
+ .assertIsEqualTo(
+ iconBottom + IndicatorVerticalPadding + NavigationBarIndicatorToLabelPadding
+ )
+ }
+
+ @Test
fun navigationBar_selectNewItem() {
rule.setMaterialContent(lightColorScheme()) {
var selectedItem by remember { mutableStateOf(0) }
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt
index ee8d7f5..0576f34 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt
@@ -676,7 +676,7 @@
modifier = Modifier.testTag(tag),
value = state.value,
onValueChange = { state.value = it },
- thumb = { sliderPositions -> recompositionCounter.OuterContent(sliderPositions) }
+ thumb = { sliderState -> recompositionCounter.OuterContent(sliderState) }
)
}
@@ -704,7 +704,7 @@
modifier = Modifier.testTag(tag),
value = state.value,
onValueChange = { state.value = it },
- track = { sliderPositions -> recompositionCounter.OuterContent(sliderPositions) }
+ track = { sliderState -> recompositionCounter.OuterContent(sliderState) }
)
}
@@ -959,7 +959,6 @@
}
}
- @OptIn(ExperimentalMaterial3Api::class)
@Test
fun rangeSlider_drag_out_of_bounds_rtl() {
val state = mutableStateOf(0f..1f)
@@ -1208,8 +1207,8 @@
@Test
fun rangeSlider_thumb_recomposition() {
val state = mutableStateOf(0f..100f)
- val startRecompositionCounter = SliderRecompositionCounter()
- val endRecompositionCounter = SliderRecompositionCounter()
+ val startRecompositionCounter = RangeSliderRecompositionCounter()
+ val endRecompositionCounter = RangeSliderRecompositionCounter()
rule.setContent {
RangeSlider(
@@ -1246,7 +1245,7 @@
@Test
fun rangeSlider_track_recomposition() {
val state = mutableStateOf(0f..100f)
- val recompositionCounter = SliderRecompositionCounter()
+ val recompositionCounter = RangeSliderRecompositionCounter()
rule.setContent {
RangeSlider(
@@ -1302,7 +1301,7 @@
}
@Stable
-class SliderRecompositionCounter {
+class RangeSliderRecompositionCounter {
var innerRecomposition = 0
var outerRecomposition = 0
@@ -1322,4 +1321,26 @@
SideEffect { ++innerRecomposition }
Text("InnerContent: ${sliderPositions.activeRange}")
}
-}
\ No newline at end of file
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Stable
+class SliderRecompositionCounter {
+ var innerRecomposition = 0
+ var outerRecomposition = 0
+
+ @Composable
+ fun OuterContent(state: SliderState) {
+ SideEffect { ++outerRecomposition }
+ Column {
+ Text("OuterContent")
+ InnerContent(state)
+ }
+ }
+
+ @Composable
+ private fun InnerContent(state: SliderState) {
+ SideEffect { ++innerRecomposition }
+ Text("InnerContent: ${state.value}")
+ }
+}
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TimePickerTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TimePickerTest.kt
index 601a161..c514c93 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TimePickerTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TimePickerTest.kt
@@ -607,11 +607,11 @@
): String {
val id = if (selection == Selection.Minute) {
- R.string.time_picker_minute_suffix
+ R.string.m3c_time_picker_minute_suffix
} else if (is24Hour) {
- R.string.time_picker_hour_24h_suffix
+ R.string.m3c_time_picker_hour_24h_suffix
} else {
- R.string.time_picker_hour_suffix
+ R.string.m3c_time_picker_hour_suffix
}
return resources.getString(id, number)
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidMenu.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidMenu.android.kt
index 3a96fdc..5948252 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidMenu.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidMenu.android.kt
@@ -17,10 +17,12 @@
package androidx.compose.material3
import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -67,9 +69,21 @@
* @param expanded whether the menu is expanded or not
* @param onDismissRequest called when the user requests to dismiss the menu, such as by tapping
* outside the menu's bounds
+ * @param modifier [Modifier] to be applied to the menu's content
* @param offset [DpOffset] to be added to the position of the menu
+ * @param properties [PopupProperties] for further customization of this popup's behavior
+ * @param content the content of this dropdown menu, typically a [DropdownMenuItem]
*/
-@Suppress("ModifierParameter")
+@OptIn(ExperimentalMaterial3Api::class)
+@Deprecated(
+ level = DeprecationLevel.HIDDEN,
+ replaceWith = ReplaceWith(
+ expression = "DropdownMenu(expanded,onDismissRequest, modifier, offset, " +
+ "rememberScrollState(), properties, content)",
+ "androidx.compose.foundation.rememberScrollState"
+ ),
+ message = "Replaced by a DropdownMenu function with a ScrollState parameter"
+)
@Composable
fun DropdownMenu(
expanded: Boolean,
@@ -78,6 +92,69 @@
offset: DpOffset = DpOffset(0.dp, 0.dp),
properties: PopupProperties = PopupProperties(focusable = true),
content: @Composable ColumnScope.() -> Unit
+) = DropdownMenu(
+ expanded = expanded,
+ onDismissRequest = onDismissRequest,
+ modifier = modifier,
+ offset = offset,
+ scrollState = rememberScrollState(),
+ properties = properties,
+ content = content
+)
+
+/**
+ * <a href="https://m3.material.io/components/menus/overview" class="external" target="_blank">Material Design dropdown menu</a>.
+ *
+ * Menus display a list of choices on a temporary surface. They appear when users interact with a
+ * button, action, or other control.
+ *
+ * 
+ *
+ * A [DropdownMenu] behaves similarly to a [Popup], and will use the position of the parent layout
+ * to position itself on screen. Commonly a [DropdownMenu] will be placed in a [Box] with a sibling
+ * that will be used as the 'anchor'. Note that a [DropdownMenu] by itself will not take up any
+ * space in a layout, as the menu is displayed in a separate window, on top of other content.
+ *
+ * The [content] of a [DropdownMenu] will typically be [DropdownMenuItem]s, as well as custom
+ * content. Using [DropdownMenuItem]s will result in a menu that matches the Material
+ * specification for menus. Also note that the [content] is placed inside a scrollable [Column],
+ * so using a [LazyColumn] as the root layout inside [content] is unsupported.
+ *
+ * [onDismissRequest] will be called when the menu should close - for example when there is a
+ * tap outside the menu, or when the back key is pressed.
+ *
+ * [DropdownMenu] changes its positioning depending on the available space, always trying to be
+ * fully visible. It will try to expand horizontally, depending on layout direction, to the end of
+ * its parent, then to the start of its parent, and then screen end-aligned. Vertically, it will
+ * try to expand to the bottom of its parent, then from the top of its parent, and then screen
+ * top-aligned. An [offset] can be provided to adjust the positioning of the menu for cases when
+ * the layout bounds of its parent do not coincide with its visual bounds. Note the offset will
+ * be applied in the direction in which the menu will decide to expand.
+ *
+ * Example usage:
+ * @sample androidx.compose.material3.samples.MenuSample
+ *
+ * Example usage with a [ScrollState] to control the menu items scroll position:
+ * @sample androidx.compose.material3.samples.MenuWithScrollStateSample
+ *
+ * @param expanded whether the menu is expanded or not
+ * @param onDismissRequest called when the user requests to dismiss the menu, such as by tapping
+ * outside the menu's bounds
+ * @param modifier [Modifier] to be applied to the menu's content
+ * @param offset [DpOffset] to be added to the position of the menu
+ * @param scrollState a [ScrollState] to used by the menu's content for items vertical scrolling
+ * @param properties [PopupProperties] for further customization of this popup's behavior
+ * @param content the content of this dropdown menu, typically a [DropdownMenuItem]
+ */
+@Composable
+fun DropdownMenu(
+ expanded: Boolean,
+ onDismissRequest: () -> Unit,
+ modifier: Modifier = Modifier,
+ offset: DpOffset = DpOffset(0.dp, 0.dp),
+ scrollState: ScrollState = rememberScrollState(),
+ properties: PopupProperties = PopupProperties(focusable = true),
+ content: @Composable ColumnScope.() -> Unit
) {
val expandedStates = remember { MutableTransitionState(false) }
expandedStates.targetState = expanded
@@ -100,6 +177,7 @@
DropdownMenuContent(
expandedStates = expandedStates,
transformOriginState = transformOriginState,
+ scrollState = scrollState,
modifier = modifier,
content = content
)
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
index 9fe9b8c..b863609 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
@@ -20,6 +20,7 @@
import android.view.View
import android.view.ViewTreeObserver
import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.waitForUpOrCancellation
@@ -28,6 +29,7 @@
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.material.icons.Icons
@@ -239,6 +241,7 @@
* @param onDismissRequest called when the user requests to dismiss the menu, such as by
* tapping outside the menu's bounds
* @param modifier the [Modifier] to be applied to this menu
+ * @param scrollState a [ScrollState] to used by the menu's content for items vertical scrolling
* @param content the content of the menu
*/
@Composable
@@ -246,6 +249,7 @@
expanded: Boolean,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
+ scrollState: ScrollState = rememberScrollState(),
content: @Composable ColumnScope.() -> Unit
) {
// TODO(b/202810604): use DropdownMenu when PopupProperties constructor is stable
@@ -277,6 +281,7 @@
DropdownMenuContent(
expandedStates = expandedStates,
transformOriginState = transformOriginState,
+ scrollState = scrollState,
modifier = modifier.exposedDropdownSize(),
content = content
)
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/Strings.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/Strings.android.kt
index 07394fc..6a6c8c3 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/Strings.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/Strings.android.kt
@@ -37,163 +37,165 @@
Strings.ExposedDropdownMenu -> resources.getString(R.string.dropdown_menu)
Strings.SliderRangeStart -> resources.getString(R.string.range_start)
Strings.SliderRangeEnd -> resources.getString(R.string.range_end)
- Strings.Dialog -> resources.getString(androidx.compose.material3.R.string.dialog)
- Strings.MenuExpanded -> resources.getString(androidx.compose.material3.R.string.expanded)
- Strings.MenuCollapsed -> resources.getString(androidx.compose.material3.R.string.collapsed)
+ Strings.Dialog -> resources.getString(androidx.compose.material3.R.string.m3c_dialog)
+ Strings.MenuExpanded ->
+ resources.getString(androidx.compose.material3.R.string.m3c_dropdown_menu_expanded)
+ Strings.MenuCollapsed ->
+ resources.getString(androidx.compose.material3.R.string.m3c_dropdown_menu_collapsed)
Strings.SnackbarDismiss -> resources.getString(
- androidx.compose.material3.R.string.snackbar_dismiss
+ androidx.compose.material3.R.string.m3c_snackbar_dismiss
)
Strings.SearchBarSearch -> resources.getString(
- androidx.compose.material3.R.string.search_bar_search
+ androidx.compose.material3.R.string.m3c_search_bar_search
)
Strings.SuggestionsAvailable ->
- resources.getString(androidx.compose.material3.R.string.suggestions_available)
+ resources.getString(androidx.compose.material3.R.string.m3c_suggestions_available)
Strings.DatePickerTitle -> resources.getString(
- androidx.compose.material3.R.string.date_picker_title
+ androidx.compose.material3.R.string.m3c_date_picker_title
)
Strings.DatePickerHeadline -> resources.getString(
- androidx.compose.material3.R.string.date_picker_headline
+ androidx.compose.material3.R.string.m3c_date_picker_headline
)
Strings.DatePickerYearPickerPaneTitle -> resources.getString(
- androidx.compose.material3.R.string.date_picker_year_picker_pane_title
+ androidx.compose.material3.R.string.m3c_date_picker_year_picker_pane_title
)
Strings.DatePickerSwitchToYearSelection -> resources.getString(
- androidx.compose.material3.R.string.date_picker_switch_to_year_selection
+ androidx.compose.material3.R.string.m3c_date_picker_switch_to_year_selection
)
Strings.DatePickerSwitchToDaySelection -> resources.getString(
- androidx.compose.material3.R.string.date_picker_switch_to_day_selection
+ androidx.compose.material3.R.string.m3c_date_picker_switch_to_day_selection
)
Strings.DatePickerSwitchToNextMonth -> resources.getString(
- androidx.compose.material3.R.string.date_picker_switch_to_next_month
+ androidx.compose.material3.R.string.m3c_date_picker_switch_to_next_month
)
Strings.DatePickerSwitchToPreviousMonth -> resources.getString(
- androidx.compose.material3.R.string.date_picker_switch_to_previous_month
+ androidx.compose.material3.R.string.m3c_date_picker_switch_to_previous_month
)
Strings.DatePickerNavigateToYearDescription -> resources.getString(
- androidx.compose.material3.R.string.date_picker_navigate_to_year_description
+ androidx.compose.material3.R.string.m3c_date_picker_navigate_to_year_description
)
Strings.DatePickerHeadlineDescription -> resources.getString(
- androidx.compose.material3.R.string.date_picker_headline_description
+ androidx.compose.material3.R.string.m3c_date_picker_headline_description
)
Strings.DatePickerNoSelectionDescription -> resources.getString(
- androidx.compose.material3.R.string.date_picker_no_selection_description
+ androidx.compose.material3.R.string.m3c_date_picker_no_selection_description
)
Strings.DatePickerTodayDescription -> resources.getString(
- androidx.compose.material3.R.string.date_picker_today_description
+ androidx.compose.material3.R.string.m3c_date_picker_today_description
)
Strings.DatePickerScrollToShowLaterYears -> resources.getString(
- androidx.compose.material3.R.string.date_picker_scroll_to_later_years
+ androidx.compose.material3.R.string.m3c_date_picker_scroll_to_later_years
)
Strings.DatePickerScrollToShowEarlierYears -> resources.getString(
- androidx.compose.material3.R.string.date_picker_scroll_to_earlier_years
+ androidx.compose.material3.R.string.m3c_date_picker_scroll_to_earlier_years
)
Strings.DateInputTitle -> resources.getString(
- androidx.compose.material3.R.string.date_input_title
+ androidx.compose.material3.R.string.m3c_date_input_title
)
Strings.DateInputHeadline -> resources.getString(
- androidx.compose.material3.R.string.date_input_headline
+ androidx.compose.material3.R.string.m3c_date_input_headline
)
Strings.DateInputLabel -> resources.getString(
- androidx.compose.material3.R.string.date_input_label
+ androidx.compose.material3.R.string.m3c_date_input_label
)
Strings.DateInputHeadlineDescription -> resources.getString(
- androidx.compose.material3.R.string.date_input_headline_description
+ androidx.compose.material3.R.string.m3c_date_input_headline_description
)
Strings.DateInputNoInputDescription -> resources.getString(
- androidx.compose.material3.R.string.date_input_no_input_description
+ androidx.compose.material3.R.string.m3c_date_input_no_input_description
)
Strings.DateInputInvalidNotAllowed -> resources.getString(
- androidx.compose.material3.R.string.date_input_invalid_not_allowed
+ androidx.compose.material3.R.string.m3c_date_input_invalid_not_allowed
)
Strings.DateInputInvalidForPattern -> resources.getString(
- androidx.compose.material3.R.string.date_input_invalid_for_pattern
+ androidx.compose.material3.R.string.m3c_date_input_invalid_for_pattern
)
Strings.DateInputInvalidYearRange -> resources.getString(
- androidx.compose.material3.R.string.date_input_invalid_year_range
+ androidx.compose.material3.R.string.m3c_date_input_invalid_year_range
)
Strings.DatePickerSwitchToCalendarMode -> resources.getString(
- androidx.compose.material3.R.string.date_picker_switch_to_calendar_mode
+ androidx.compose.material3.R.string.m3c_date_picker_switch_to_calendar_mode
)
Strings.DatePickerSwitchToInputMode -> resources.getString(
- androidx.compose.material3.R.string.date_picker_switch_to_input_mode
+ androidx.compose.material3.R.string.m3c_date_picker_switch_to_input_mode
)
Strings.DateRangePickerTitle -> resources.getString(
- androidx.compose.material3.R.string.date_range_picker_title
+ androidx.compose.material3.R.string.m3c_date_range_picker_title
)
Strings.DateRangePickerStartHeadline -> resources.getString(
- androidx.compose.material3.R.string.date_range_picker_start_headline
+ androidx.compose.material3.R.string.m3c_date_range_picker_start_headline
)
Strings.DateRangePickerEndHeadline -> resources.getString(
- androidx.compose.material3.R.string.date_range_picker_end_headline
+ androidx.compose.material3.R.string.m3c_date_range_picker_end_headline
)
Strings.DateRangePickerScrollToShowNextMonth -> resources.getString(
- androidx.compose.material3.R.string.date_range_picker_scroll_to_next_month
+ androidx.compose.material3.R.string.m3c_date_range_picker_scroll_to_next_month
)
Strings.DateRangePickerScrollToShowPreviousMonth -> resources.getString(
- androidx.compose.material3.R.string.date_range_picker_scroll_to_previous_month
+ androidx.compose.material3.R.string.m3c_date_range_picker_scroll_to_previous_month
)
Strings.DateRangePickerDayInRange -> resources.getString(
- androidx.compose.material3.R.string.date_range_picker_day_in_range
+ androidx.compose.material3.R.string.m3c_date_range_picker_day_in_range
)
Strings.DateRangeInputTitle -> resources.getString(
- androidx.compose.material3.R.string.date_range_input_title
+ androidx.compose.material3.R.string.m3c_date_range_input_title
)
Strings.DateRangeInputInvalidRangeInput -> resources.getString(
- androidx.compose.material3.R.string.date_range_input_invalid_range_input
+ androidx.compose.material3.R.string.m3c_date_range_input_invalid_range_input
)
Strings.BottomSheetDragHandleDescription -> resources.getString(
- androidx.compose.material3.R.string.bottom_sheet_drag_handle_description
+ androidx.compose.material3.R.string.m3c_bottom_sheet_drag_handle_description
)
Strings.BottomSheetPartialExpandDescription -> resources.getString(
- androidx.compose.material3.R.string.bottom_sheet_collapse_description
+ androidx.compose.material3.R.string.m3c_bottom_sheet_collapse_description
)
Strings.BottomSheetDismissDescription -> resources.getString(
- androidx.compose.material3.R.string.bottom_sheet_dismiss_description
+ androidx.compose.material3.R.string.m3c_bottom_sheet_dismiss_description
)
Strings.BottomSheetExpandDescription -> resources.getString(
- androidx.compose.material3.R.string.bottom_sheet_expand_description
+ androidx.compose.material3.R.string.m3c_bottom_sheet_expand_description
)
Strings.TooltipLongPressLabel -> resources.getString(
- androidx.compose.material3.R.string.tooltip_long_press_label
+ androidx.compose.material3.R.string.m3c_tooltip_long_press_label
)
Strings.TimePickerAM -> resources.getString(
- androidx.compose.material3.R.string.time_picker_am)
+ androidx.compose.material3.R.string.m3c_time_picker_am)
Strings.TimePickerPM -> resources.getString(
- androidx.compose.material3.R.string.time_picker_pm)
+ androidx.compose.material3.R.string.m3c_time_picker_pm)
Strings.TimePickerPeriodToggle -> resources.getString(
- androidx.compose.material3.R.string.time_picker_period_toggle_description)
+ androidx.compose.material3.R.string.m3c_time_picker_period_toggle_description)
Strings.TimePickerMinuteSelection -> resources.getString(
- androidx.compose.material3.R.string.time_picker_minute_selection)
+ androidx.compose.material3.R.string.m3c_time_picker_minute_selection)
Strings.TimePickerHourSelection -> resources.getString(
- androidx.compose.material3.R.string.time_picker_hour_selection)
+ androidx.compose.material3.R.string.m3c_time_picker_hour_selection)
Strings.TimePickerHourSuffix -> resources.getString(
- androidx.compose.material3.R.string.time_picker_hour_suffix)
+ androidx.compose.material3.R.string.m3c_time_picker_hour_suffix)
Strings.TimePickerMinuteSuffix -> resources.getString(
- androidx.compose.material3.R.string.time_picker_minute_suffix)
+ androidx.compose.material3.R.string.m3c_time_picker_minute_suffix)
Strings.TimePicker24HourSuffix -> resources.getString(
- androidx.compose.material3.R.string.time_picker_hour_24h_suffix)
+ androidx.compose.material3.R.string.m3c_time_picker_hour_24h_suffix)
Strings.TimePickerHour -> resources.getString(
- androidx.compose.material3.R.string.time_picker_hour)
+ androidx.compose.material3.R.string.m3c_time_picker_hour)
Strings.TimePickerMinute -> resources.getString(
- androidx.compose.material3.R.string.time_picker_minute)
+ androidx.compose.material3.R.string.m3c_time_picker_minute)
Strings.TimePickerHourTextField -> resources.getString(
- androidx.compose.material3.R.string.time_picker_hour_text_field)
+ androidx.compose.material3.R.string.m3c_time_picker_hour_text_field)
Strings.TimePickerMinuteTextField -> resources.getString(
- androidx.compose.material3.R.string.time_picker_minute_text_field)
+ androidx.compose.material3.R.string.m3c_time_picker_minute_text_field)
Strings.TooltipPaneDescription -> resources.getString(
- androidx.compose.material3.R.string.tooltip_pane_description)
+ androidx.compose.material3.R.string.m3c_tooltip_pane_description)
else -> ""
}
}
diff --git a/compose/material3/material3/src/androidMain/res/values-af/strings.xml b/compose/material3/material3/src/androidMain/res/values-af/strings.xml
deleted file mode 100644
index beb91f8..0000000
--- a/compose/material3/material3/src/androidMain/res/values-af/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialoog"</string>
- <string name="expanded" msgid="5974471714631304645">"Uitgevou"</string>
- <string name="collapsed" msgid="5389587048670450460">"Ingevou"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Maak toe"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Soek"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Voorstelle hieronder"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Kies datum"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Geselekteerde datum"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Skakel oor na kies van ’n jaar"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Swiep om ’n jaar te kies of tik om terug te skakel om ’n dag te kies"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Verander na volgende maand"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Verander na vorige maand"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Gaan na jaar %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Huidige keuse: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Geen"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Vandag"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Jaarkieser sigbaar"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Kies datum"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Datum wat ingevoer is"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Datum"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Datum wat ingevoer is: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Geen"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Datum word nie toegelaat nie: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Datum pas nie by die verwagte patroon nie: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Datum val buite die omvang van die verwagte jaartal %1$s-%2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Skakel oor na kalenderinvoermodus"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Skakel oor na teksinvoermodus"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Rollees om later jare te wys"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Rollees om vroeër jare te wys"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Kies datums"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Begindatum"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Einddatum"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Rollees om die volgende maand te wys"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Rollees om die vorige maand te wys"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Binne reikwydte"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Voer datums in"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Ongeldige datumreeksinvoer"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Sleephandvatsel"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Vou onderste blad in"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Maak onderste blad toe"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Vou onderste blad uit"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Nutswenk"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Wys nutswenk"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"nm."</string>
- <string name="time_picker_am" msgid="5096144640014509074">"vm."</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Kies vm. of nm."</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Kies uur"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Kies minute"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d uur"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d uur"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minute"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minuut"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Uur"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"vir minute"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"vir uur"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-am/strings.xml b/compose/material3/material3/src/androidMain/res/values-am/strings.xml
deleted file mode 100644
index c8749d6..0000000
--- a/compose/material3/material3/src/androidMain/res/values-am/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"መገናኛ"</string>
- <string name="expanded" msgid="5974471714631304645">"ተዘርግቷል"</string>
- <string name="collapsed" msgid="5389587048670450460">"ተሰብስቧል"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"አሰናብት"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"ፍለጋ"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"የአስተያየት ጥቆማዎች ከታች"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"ቀን ይምረጡ"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"የተመረጠው ቀን"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"ወደ ዓመት መምረጥ ቀይር"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"ዓመት ለመምረጥ ያንሸራትቱ ወይም ወደ ቀንን መምረጥ መልሶ ለመቀየር መታ ያድርጉ"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"ወደ የሚቀጥለው ወር ቀይር"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"ወደ ቀዳሚው ወር ቀይር"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"ወደ ዓመት %1$s ያስሱ"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"የአሁን ምርጫ፦ %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"ምንም"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"ዛሬ"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"ዓመት መራጭ ይታያል"</string>
- <string name="date_input_title" msgid="3010396677286327048">"ቀን ይምረጡ"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"የገባው ቀን"</string>
- <string name="date_input_label" msgid="5194825853981987218">"ቀን"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"የገባው ቀን፦ %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"ምንም"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"ቀን አልተፈቀደም፦ %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"ቀኑ ከተጠበቀው ስርዓተ ጥለት ጋር አይዛመድም፦ %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"ቀን ከተጠበቀው የዓመት ክልል ውጪ ነው %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"ወደ የቀን መቁጠሪያ ግቤት ሁነታ ቀይር"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"ወደ የጽሁፍ ግቤት ሁነታ ቀይር"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"ከዚህ በኋላ ያሉ ዓመታትን ለማሳየት ይሸብልሉ"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"ቀደም ያሉ ዓመታትን ለማሳየት ይሸብልሉ"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"ቀናትን ይምረጡ"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"የመጀመሪያ ቀን"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"የማብቂያ ቀን"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"ቀጣዩን ወር ለማሳየት ይሸብልሉ"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"ቀዳሚውን ወር ለማሳየት ይሸብልሉ"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"በክልል ውስጥ"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"ቀናትን ያስገቡ"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"ልክ ያልሆነ የቀን ክልል ግቤት"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"መያዣ ይጎትቱ"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"የግርጌ ሉህን ይሰብስቡ"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"የግርጌ ሉህን ይሰብስቡ"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"የግርጌ ሉህ ይዘርጉ"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"የመሣሪያ ጥቆማ"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"መሣሪያ ጥቆማን አሳይ"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"ከሰዓት"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"ጠዋት"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"ጠዋት ወይም ከሰዓትን ይምረጡ"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"ሰዓት ምረጥ"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"ደቂቃዎች ምረጥ"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d ሰዓት"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d ሰዓታት"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d ደቂቃዎች"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"ደቂቃ"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"ሰዓት"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"ለደቂቃዎች"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ለሰዓት"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ar/strings.xml b/compose/material3/material3/src/androidMain/res/values-ar/strings.xml
deleted file mode 100644
index ae5ecc0..0000000
--- a/compose/material3/material3/src/androidMain/res/values-ar/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"مربّع حوار"</string>
- <string name="expanded" msgid="5974471714631304645">"موسَّع"</string>
- <string name="collapsed" msgid="5389587048670450460">"مصغَّر"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"إغلاق"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"بحث"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"إليك الاقتراحات:"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"اختيار تاريخ"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"التاريخ المحدَّد"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"التبديل لاختيار سنة"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"مرِّر سريعًا لتحديد عام، أو انقر للرجوع إلى تحديد يوم."</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"التغيير إلى الشهر التالي"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"التغيير إلى الشهر السابق"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"الانتقال إلى عام %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"التحديد الحالي: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"بدون تاريخ"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"اليوم"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"أداة اختيار الأعوام مرئية"</string>
- <string name="date_input_title" msgid="3010396677286327048">"اختيار تاريخ"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"التاريخ الذي تم إدخاله"</string>
- <string name="date_input_label" msgid="5194825853981987218">"التاريخ"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"التاريخ الذي تم إدخاله: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"بدون تاريخ"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"التاريخ غير مسموح به: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"لا يتوافق التاريخ مع النمط المتوقَّع: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"التاريخ خارج نطاق الأعوام المتوقَّع: %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"التبديل إلى وضع \"الإدخال في التقويم\""</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"التبديل إلى وضع \"إدخال النص\""</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"انتقِل للأسفل/للأعلى لإظهار السنوات التالية."</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"انتقِل للأسفل/للأعلى لإظهار السنوات السابقة."</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"اختيار التواريخ"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"تاريخ البدء"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"تاريخ الانتهاء"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"انتقِل للأسفل/للأعلى لإظهار الشهر التالي."</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"انتقِل للأسفل/للأعلى لإظهار الشهر السابق."</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"في النطاق"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"إدخال التواريخ"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"إدخال نطاق زمني غير صالح"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"مقبض السحب"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"تصغير البطاقة السفلية"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"إغلاق البطاقة السفلية"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"توسيع البطاقة السفلية"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"تلميح"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"إظهار التلميح"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"م"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"ص"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"يُرجى اختيار ص (صباحًا) أو م (مساءً)."</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"اختيار الساعة"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"اختيار الدقائق"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"الساعة %1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d ساعة"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d دقيقة"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"دقيقة"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"ساعة"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"لمدة دقائق"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"لمدة ساعة"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-as/strings.xml b/compose/material3/material3/src/androidMain/res/values-as/strings.xml
deleted file mode 100644
index 7bd785c..0000000
--- a/compose/material3/material3/src/androidMain/res/values-as/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"ডায়ল’গ"</string>
- <string name="expanded" msgid="5974471714631304645">"বিস্তাৰ কৰা আছে"</string>
- <string name="collapsed" msgid="5389587048670450460">"সংকোচন কৰা আছে"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"অগ্ৰাহ্য কৰক"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"সন্ধান কৰক"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"তলত পৰামৰ্শ দেখুওৱা হৈছে"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"তাৰিখ বাছনি কৰক"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"বাছনি কৰা তাৰিখ"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"বছৰ বাছনি কৰাৰ ছুইচ"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"এটা বছৰ বাছনি কৰিবলৈ ছোৱাইপ কৰক অথবা এটা দিন বাছনি কৰাৰ সুবিধাটোলৈ উভতি যাবলৈ টিপক"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"পৰৱৰ্তী মাহলৈ সলনি কৰক"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"পূৰ্বৱৰ্তী মাহলৈ সলনি কৰক"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"বৰ্ষ %1$sলৈ নেভিগে’ট কৰক"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"বৰ্তমানৰ বাছনি: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"একো নাই"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"আজি"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"বছৰ বাছনিকৰ্তা দৃশ্যমান"</string>
- <string name="date_input_title" msgid="3010396677286327048">"তাৰিখ বাছনি কৰক"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"দিয়া তাৰিখ"</string>
- <string name="date_input_label" msgid="5194825853981987218">"তাৰিখ"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"দিয়া তাৰিখ: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"একো নাই"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"অনুমোদিত নোহোৱা তাৰিখ: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"তাৰিখটো এই প্ৰত্যাশিত আৰ্হিটোৰ সৈতে মিলা নাই: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"তাৰিখটো প্ৰত্যাশিত বছৰৰ পৰিসৰ %1$s - %2$sৰ বাহিৰৰ"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"কেলেণ্ডাৰ ইনপুট ম’ডলৈ সলনি কৰক"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"পাঠ ইনপুট ম’ডলৈ সলনি কৰক"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"পাছৰ বছৰবোৰ দেখুৱাবলৈ স্ক্ৰ’ল কৰক"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"আগৰ বছৰবোৰ দেখুৱাবলৈ স্ক্ৰ’ল কৰক"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"তাৰিখ বাছনি কৰক"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"আৰম্ভণিৰ তাৰিখ"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"সমাপ্তিৰ তাৰিখ"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"পৰৱৰ্তী মাহটো দেখুৱাবলৈ স্ক্ৰ’ল কৰক"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"পূৰ্বৱৰ্তী মাহটো দেখুৱাবলৈ স্ক্ৰ’ল কৰক"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"পৰিসৰৰ ভিতৰত আছে"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"তাৰিখ দিয়ক"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"অমান্য তাৰিখৰ পৰিসৰৰ ইনপুট"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ড্ৰেগ হেণ্ডেল"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"তলৰ শ্বীটখন সংকোচন কৰক"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"তলৰ শ্বীটখন অগ্ৰাহ্য কৰক"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"তলৰ শ্বীটখন বিস্তাৰ কৰক"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"টুলটিপ"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"টুলটিপ দেখুৱাওক"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"পৰাহ্ন"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"পুৱা"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"পূৰ্বাহ্ন অথবা অপৰাহ্ন বাছনি কৰক"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"ঘণ্টা বাছনি কৰক"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"মিনিট বাছনি কৰক"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d বাজিছে"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%d ঘণ্টা"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%d মিনিট"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"মিনিট"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"ঘণ্টা"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"মিনিটৰ বাবে"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ঘণ্টাৰ বাবে"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-az/strings.xml b/compose/material3/material3/src/androidMain/res/values-az/strings.xml
deleted file mode 100644
index da185a0..0000000
--- a/compose/material3/material3/src/androidMain/res/values-az/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialoq"</string>
- <string name="expanded" msgid="5974471714631304645">"Genişləndirilib"</string>
- <string name="collapsed" msgid="5389587048670450460">"Yığcamlaşdırılıb"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Qapadın"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Axtarış"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Təkliflər aşağıdadır"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Tarix seçin"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Seçilmiş tarix"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"İl seçiminə keçin"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"İl seçmək üçün sürüşdürün və ya gün seçiminə qayıtmaq üçün toxunun"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Növbəti aya dəyişin"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Əvvəlki aya dəyişin"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Bu ilə keçin: %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Cari seçim: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Heç biri"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Bu gün"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"İl seçicisi görünür"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Tarix seçin"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Daxil edilmiş tarix"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Tarix"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Daxil edilmiş tarix: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Heç biri"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Tarixə icazə verilmir: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Tarix gözlənilən modelə uyğun gəlmir: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Tarix gözlənilən il aralığından kənardır: %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Təqvim daxiletmə rejiminə keçin"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Mətn daxiletmə rejiminə keçin"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Sonrakı illəri göstərmək üçün sürüşdürün"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Əvvəlki illəri göstərmək üçün sürüşdürün"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Tarixləri seçin"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Başlama tarixi"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Bitmə tarixi"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Növbəti ayı göstərmək üçün sürüşdürün"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Əvvəlki ayı göstərmək üçün sürüşdürün"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Bu aralıqda"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Tarixləri daxil edin"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Yanlış tarix aralığı daxiletməsi"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Dəstəyi çəkin"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Aşağıdakı vərəqi yığcamlaşdırın"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Aşağıdakı vərəqi rədd edin"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Aşağıdakı vərəqi genişləndirin"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Alət izahı"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"İpucu göstərin"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Gündüz və ya axşam seçin"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Saatı seçin"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Dəqiqə seçin"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"Saat: %1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d saat"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d dəqiqə"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Dəqiqə"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Saat"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"dəqiqəlik"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"saatlıq"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-b+sr+Latn/strings.xml b/compose/material3/material3/src/androidMain/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index be53fbf..0000000
--- a/compose/material3/material3/src/androidMain/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dijalog"</string>
- <string name="expanded" msgid="5974471714631304645">"Prošireno je"</string>
- <string name="collapsed" msgid="5389587048670450460">"Skupljeno je"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Odbacite"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Pretraga"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Predlozi su u nastavku"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Izaberite datum"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Izabrani datum"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Pređite na izbor godine"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Prevucite da biste izabrali godinu ili dodirnite da biste se vratili na izbor dana"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Pređite na sledeći mesec"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Pređite na prethodni mesec"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Idite na godinu: %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Aktuelni izbor: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Ništa"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Danas"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Vidljiv birač godina"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Izaberite datum"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Uneti datum"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Datum"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Uneti datum: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Ništa"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Datum nije dozvoljen: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Datum ne odgovara očekivanom šablonu: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Datum je izvan očekivanog opsega godina %1$s – %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Pređite na režim unosa u Kalendaru"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Pređite na režim unosa teksta"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Pomerajte da bi se prikazale kasnije godine"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Pomerajte da bi se prikazale ranije godine"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Izaberite datume"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Datum početka"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Datum završetka"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Pomerajte da bi se prikazao sledeći mesec"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Pomerajte da bi se prikazao prethodni mesec"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"U dometu"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Unesite datume"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Unos opsega datuma je nevažeći"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Identifikator za prevlačenje"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Skupi donju tabelu"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Odbaci donju tabelu"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Proširi donju tabelu"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Objašnjenje"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Prikaži objašnjenje"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"po"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"pr"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Izaberite pre podne ili po podne"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Izaberite sat"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Izaberite minute"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d č"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d č"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d min"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minut"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Sat"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"za minute"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"za sate"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-be/strings.xml b/compose/material3/material3/src/androidMain/res/values-be/strings.xml
deleted file mode 100644
index 78697c3..0000000
--- a/compose/material3/material3/src/androidMain/res/values-be/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Дыялогавае акно"</string>
- <string name="expanded" msgid="5974471714631304645">"Разгорнута"</string>
- <string name="collapsed" msgid="5389587048670450460">"Згорнута"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Закрыць"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Пошук"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Прапановы ўнізе"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Выберыце дату"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Выбраная дата"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Перайсці да выбару года"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Правядзіце пальцам, каб выбраць год, або націсніце, каб вярнуцца да выбару даты"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Перайсці да наступнага месяца"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Перайсці да папярэдняга месяца"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Перайсці ў год %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Бягучы выбар: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Не выбрана"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Сёння"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Бачны інструмент выбару года"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Выберыце дату"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Уведзеная дата"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Дата"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Уведзеная дата: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Без абмежаванняў"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Дата забаронена: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Дата не адпавядае ўзору: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Дата выходзіць за дазволены дыяпазон гадоў %1$s-%2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Пераключыцца ў рэжым уводу \"Каляндар\""</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Пераключыцца ў рэжым уводу \"Тэкст\""</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Прагартайце экран, каб праглядзець пазнейшыя гады"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Прагартайце экран, каб праглядзець ранейшыя гады"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Выберыце даты"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Дата пачатку"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Дата заканчэння"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Прагартайце экран, каб праглядзець наступны месяц"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Прагартайце экран, каб праглядзець мінулы месяц"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"У зоне дасягальнасці"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Увядзіце даты"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Уведзены няправільны дыяпазон дат"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Маркер перацягвання"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Згарнуць ніжні аркуш"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Закрыць ніжні аркуш"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Разгарнуць ніжні аркуш"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Падказка"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Паказваць усплывальную падказку"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"пасля паўдня"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"да паўдня"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Выберыце AM (да паўдня) або PM (пасля паўдня)"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Выберыце гадзіны"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Выберыце хвіліны"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d гадз"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d гадз"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d хв"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Хвіліны"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Гадзіны"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"хвіліны"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"гадзіны"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-bg/strings.xml b/compose/material3/material3/src/androidMain/res/values-bg/strings.xml
deleted file mode 100644
index 63b1abf..0000000
--- a/compose/material3/material3/src/androidMain/res/values-bg/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Диалогов прозорец"</string>
- <string name="expanded" msgid="5974471714631304645">"Разгънато"</string>
- <string name="collapsed" msgid="5389587048670450460">"Свито"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Отхвърляне"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Търсене"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Предложенията са по-долу"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Избиране на дата"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Избрана дата"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Превключване към избиране на година"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Прекарайте пръст, за да изберете година, или докоснете, за да се върнете към избора на ден"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Преминаване към следващия месец"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Преминаване към предишния месец"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Навигиране до %1$s година"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Текущ избор: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Без"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Днес"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Инструментът за избор на година е видим"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Избиране на дата"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Въведена дата"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Дата"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Въведена дата: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Няма"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Датата не е разрешена: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Датата не е в очаквания формат: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Датата е извън очаквания годишен диапазон: %1$s – %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Превключване към режим за въвеждане в календар"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Превключване към режим за въвеждане на текст"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Превъртете, за да видите следващите години"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Превъртете, за да видите миналите години"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Избиране на дати"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Начална дата"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Крайна дата"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Превъртете, за да видите следващия месец"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Превъртете, за да видите предишния месец"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"В диапазона"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Въведете дати"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Въведен е невалиден период от време"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Манипулатор за преместване с плъзгане"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Свиване на долния лист"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Отхвърляне на долния лист"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Разгъване на долния лист"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Подсказка"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Показване на подсказка"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Изберете AM или PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Избиране на час"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Избиране на минути"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d часа"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d часа"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d минути"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Минута"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Час"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"за минутите"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"за часа"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-bn/strings.xml b/compose/material3/material3/src/androidMain/res/values-bn/strings.xml
deleted file mode 100644
index 70935fe..0000000
--- a/compose/material3/material3/src/androidMain/res/values-bn/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"ডায়ালগ বক্স"</string>
- <string name="expanded" msgid="5974471714631304645">"বড় করা হয়েছে"</string>
- <string name="collapsed" msgid="5389587048670450460">"আড়াল করা হয়েছে"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"বাতিল করুন"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"সার্চ করুন"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"নিচে দেওয়া সাজেশন"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"তারিখ বেছে নিন"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"বেছে নেওয়া তারিখ"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"কোনও একটি বছর বেছে নিতে পাল্টে নিন"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"একটি বছর বেছে নিতে সোয়াইপ করুন অথবা কোনও একটি দিন বাছতে ফিরে গিয়ে সুইচে ট্যাপ করুন"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"আগামী মাসে পরিবর্তন করুন"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"আগের মাসে পরিবর্তন করুন"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"নেভিগেট করে %1$s বছরে যান"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"বর্তমানে বেছে নেওয়া হয়েছে: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"কোনওটিই নয়"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"আজ"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"বছর বেছে নেওয়ার তালিকা দেখা যাচ্ছে"</string>
- <string name="date_input_title" msgid="3010396677286327048">"তারিখ বেছে নিন"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"উল্লেখ করা তারিখ"</string>
- <string name="date_input_label" msgid="5194825853981987218">"তারিখ"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"উল্লেখ করা তারিখ: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"কোনওটিই নয়"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"এই তারিখ লেখা যাবে না: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"প্রত্যাশিত প্যাটার্নের সাথে তারিখ মিলছে না: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"প্রত্যাশিত বছরের রেঞ্জের বাইরের তারিখ %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"\'ক্যালেন্ডার ইনপুট\' মোডে বদল করুন"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"\'টেক্সট ইনপুট\' মোডে বদল করুন"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"পরের বছর দেখতে স্ক্রল করুন"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"আগের বছর দেখতে স্ক্রল করুন"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"তারিখ বেছে নিন"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"শুরু হওয়ার তারিখ"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"শেষ হওয়ার তারিখ"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"পরের মাস দেখতে স্ক্রল করুন"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"আগের মাস দেখতে স্ক্রল করুন"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"সীমার মধ্যে"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"তারিখ লিখুন"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"তারিখের ব্যাপ্তি সম্পর্কিত ইনপুট ভুল দেওয়া আছে"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"টেনে আনার হ্যান্ডেল"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"স্ক্রিনের নিচে অ্যাটাচ করা শিট আড়াল করুন"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"স্ক্রিনের নিচে অ্যাটাচ করা শিট বাতিল করুন"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"স্ক্রিনের নিচে অ্যাটাচ করা শিট বড় করুন"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"টুলটিপ"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"টুলটিপ দেখান"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"AM বা PM বেছে নিন"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"ঘণ্টা বেছে নিন"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"মিনিট বেছে নিন"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d টা"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d ঘণ্টা"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d মিনিট"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"মিনিট"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"ঘণ্টা"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"এত মিনিটের জন্য"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"এত ঘণ্টার জন্য"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-bs/strings.xml b/compose/material3/material3/src/androidMain/res/values-bs/strings.xml
deleted file mode 100644
index 26c35b0..0000000
--- a/compose/material3/material3/src/androidMain/res/values-bs/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dijaloški okvir"</string>
- <string name="expanded" msgid="5974471714631304645">"Prošireno"</string>
- <string name="collapsed" msgid="5389587048670450460">"Suženo"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Odbacivanje"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Pretraživanje"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Prijedlozi su u nastavku"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Odabir datuma"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Odabrani datum"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Prebaci na odabir godine"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Prevucite da odaberete godinu ili dodirnite da se vratite na odabir dana"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Promijeni na sljedeći mjesec"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Promijeni na prethodni mjesec"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Odlazak na %1$s. godinu"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Trenutni odabir: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Ništa"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Danas"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Birač godine je vidljiv"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Odaberite datum"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Unesen je datum"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Datum"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Unesen je datum: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Ništa"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Datum nije dozvoljen: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Datumi se ne podudaraju s očekivanim obrascem: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Datum je izvan očekivanog raspona %1$s – %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Prebacivanje na način rada unosa kalendara"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Prebacivanje na način rada unosa teksta"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Kliznite da se prikažu kasnije godine"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Kliznite da se prikažu ranije godine"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Odaberite datume"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Datum početka"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Datum završetka"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Kliznite da se prikaže sljedeći mjesec"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Kliznite da se prikaže prethodni mjesec"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"U dometu"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Unesite datume"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Nevažeći unos raspona datuma"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Ručica za prevlačenje"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Sužavanje donje tabele"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Odbacivanje donje tabele"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Proširivanje donje tabele"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Skočni opis"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Prikaži skočni opis"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"poslijepodne"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"prij."</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Odaberite prijepodne ili poslijepodne"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Odaberite sate"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Odaberite minute"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d h"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d h"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d min"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minuta"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Sat"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"za minute"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"za sat"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ca/strings.xml b/compose/material3/material3/src/androidMain/res/values-ca/strings.xml
deleted file mode 100644
index a694aaf4..0000000
--- a/compose/material3/material3/src/androidMain/res/values-ca/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Diàleg"</string>
- <string name="expanded" msgid="5974471714631304645">"S\'ha desplegat"</string>
- <string name="collapsed" msgid="5389587048670450460">"S\'ha replegat"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Ignora"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Cerca"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Suggeriments a continuació"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Selecciona la data"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Data seleccionada"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Canvia a la selecció de l\'any"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Llisca per seleccionar un any o toca per tornar a seleccionar un dia"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Canvia al mes següent"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Canvia al mes anterior"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Navega fins a l\'any %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Selecció actual: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Cap"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Avui"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Selector d\'any visible"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Selecciona la data"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Data introduïda"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Data"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Data introduïda: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Cap"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Data no permesa: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"La data no coincideix amb el patró esperat: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"La data no es troba dins de l\'interval d\'anys esperat: %1$s-%2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Canvia al mode d\'introducció de dades del calendari"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Canvia al mode d\'introducció de text"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Desplaça\'t per mostrar els darrers anys"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Desplaça\'t per mostrar els anys anteriors"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Selecciona les dates"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Data d\'inici"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Data de finalització"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Desplaça\'t per mostrar el mes següent"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Desplaça\'t per mostrar el mes anterior"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Dins de l\'interval"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Introdueix les dates"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"S\'ha introduït un interval de dades no vàlid"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Ansa per arrossegar"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Replega el full inferior"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Ignora el full inferior"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Desplega el full inferior"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Descripció emergent"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Mostra la descripció emergent"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Selecciona AM o PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Selecciona l\'hora"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Selecciona els minuts"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d en punt"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d hores"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minuts"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minut"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Hora"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"per als minuts"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"per a l\'hora"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-cs/strings.xml b/compose/material3/material3/src/androidMain/res/values-cs/strings.xml
deleted file mode 100644
index a8ff6c0..0000000
--- a/compose/material3/material3/src/androidMain/res/values-cs/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialogové okno"</string>
- <string name="expanded" msgid="5974471714631304645">"Rozbaleno"</string>
- <string name="collapsed" msgid="5389587048670450460">"Sbaleno"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Zavřít"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Hledat"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Návrh je níže"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Vybrat datum"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Vybrané datum"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Přepnout na výběr roku"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Přejetím prstem vyberte rok nebo se klepnutím vraťte k výběru dne"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Přejít na další měsíc"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Přejít na předchozí měsíc"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Přejít na rok %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Aktuální výběr: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Žádné"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Dnes"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Je vidět výběr roku"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Vybrat datum"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Zadané datum"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Datum"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Zadané datum: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Žádné"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Nepovolené datum: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Datum neodpovídá očekávanému vzoru: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Datum mimo očekávaný rozsah roků %1$s–%2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Přepnout na režim zadávání do kalendáře"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Přepnout na režim zadávání textu"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Posunutím zobrazíte pozdější roky"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Posunutím zobrazíte předchozí roky"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Vyberte data"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Datum zahájení"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Datum ukončení"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Posunutím zobrazíte další měsíc"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Posunutím zobrazíte předchozí měsíc"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"V rozsahu"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Zadejte data"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Neplatné období"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Úchyt pro přetažení"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Sbalit spodní tabulku"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Zavřít spodní tabulku"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Rozbalit spodní tabulku"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Popisek"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Zobrazit popisek"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Vyberte AM nebo PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Vybrat hodinu"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Vyberte minuty"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d hodin"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d hodin"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minut"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minuta"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Hodina"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"pro minuty"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"pro hodinu"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-da/strings.xml b/compose/material3/material3/src/androidMain/res/values-da/strings.xml
deleted file mode 100644
index d87fd19..0000000
--- a/compose/material3/material3/src/androidMain/res/values-da/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialogboks"</string>
- <string name="expanded" msgid="5974471714631304645">"Udvidet"</string>
- <string name="collapsed" msgid="5389587048670450460">"Skjult"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Afvis"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Søg"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Forslag nedenfor"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Vælg dato"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Valgt dato"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Skift til valg af år"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Stryg for at vælge et år, eller tryk for at skifte tilbage til datovælgeren"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Skift til næste måned"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Skift til forrige måned"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Naviger til år %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Aktuelt valg: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Ingen"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"I dag"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Årsvælgeren er synlig"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Vælg dato"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Angivet dato"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Dato"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Angivet dato: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Ingen"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Datoen er ikke tilladt: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Datoen svarer ikke til det forventede format: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Datoen er uden for det forventede årsinterval: %1$s-%2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Skift til input-tilstand for kalender"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Skift til input-tilstand for tekst"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Rul for at vise senere år"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Rul for at vise tidligere år"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Vælg datoer"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Startdato"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Slutdato"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Rul for at vise næste måned"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Rul for at vise forrige måned"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Inden for de valgte dage"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Angiv datoer"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Ugyldig angivelse af datainterval"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Håndtag"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Skjul felt i bunden"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Luk felt i bunden"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Udvid felt i bunden"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Værktøjstip"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Se værktøjstip"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Vælg AM eller PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Vælg time"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Vælg minutter"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"kl. %1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d timer"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutter"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minut"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Time"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"for minutter"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"for time"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-de/strings.xml b/compose/material3/material3/src/androidMain/res/values-de/strings.xml
deleted file mode 100644
index 4052a8f..0000000
--- a/compose/material3/material3/src/androidMain/res/values-de/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialogfeld"</string>
- <string name="expanded" msgid="5974471714631304645">"Maximiert"</string>
- <string name="collapsed" msgid="5389587048670450460">"Minimiert"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Schließen"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Suchen"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Vorschläge unten"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Datum auswählen"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Ausgewähltes Datum"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Zur Jahresauswahl wechseln"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Wischen, um ein Jahr auszuwählen, oder tippen, um zur Tagesauswahl zurückzukehren"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Zum nächsten Monat wechseln"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Zum vorherigen Monat wechseln"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Zum Jahr %1$s wechseln"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Aktuelle Auswahl: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Keine"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Heute"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Jahresauswahl sichtbar"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Datum auswählen"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Eingabedatum:"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Datum"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Eingabedatum: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Kein Datum"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Unzulässiges Datum: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Datum entspricht nicht dem erwarteten Format: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Datum liegt außerhalb des erwarteten Jahresbereichs (%1$s–%2$s)"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"In den Kalendereingabemodus wechseln"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"In den Texteingabemodus wechseln"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Zum Ansehen nachfolgender Jahre scrollen"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Zum Ansehen vorheriger Jahre scrollen"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Daten auswählen"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Startdatum"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Enddatum"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Zum Ansehen des nächsten Monats scrollen"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Zum Ansehen des vorherigen Monats scrollen"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Im Zeitraum"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Daten eingeben"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Angegebener Zeitraum ungültig"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Ziehpunkt"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Ansicht am unteren Rand minimieren"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Ansicht am unteren Rand schließen"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Ansicht am unteren Rand maximieren"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Kurzinfo"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Kurzinfo anzeigen"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"AM oder PM auswählen"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Stunde auswählen"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Minuten auswählen"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d Uhr"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d Stunden"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d Minuten"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Stunde"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"für Minuten"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"für Stunde"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-el/strings.xml b/compose/material3/material3/src/androidMain/res/values-el/strings.xml
deleted file mode 100644
index 06b90b8..0000000
--- a/compose/material3/material3/src/androidMain/res/values-el/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Παράθυρο διαλόγου"</string>
- <string name="expanded" msgid="5974471714631304645">"Ανεπτυγμένο"</string>
- <string name="collapsed" msgid="5389587048670450460">"Συμπτυγμένο"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Παράβλεψη"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Αναζήτηση"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Προτάσεις παρακάτω"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Επιλογή ημερομηνίας"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Επιλεγμένη ημερομηνία"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Εναλλαγή σε επιλογή έτους"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Σύρετε για να επιλέξετε ένα έτος ή πατήστε για να επιστρέψετε στην επιλογή ημέρας."</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Αλλαγή στον επόμενο μήνα"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Αλλαγή στον προηγούμενο μήνα"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Μετάβαση στο έτος %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Τρέχουσα επιλογή: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Καμία"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Σήμερα"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Το εργαλείο επιλογής έτους είναι ορατό"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Επιλογή ημερομηνίας"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Εισαγωγή ημερομηνίας"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Ημερομηνία"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Εισαγωγή ημερομηνίας: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Καμία"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Η ημερομηνία δεν επιτρέπεται: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Η ημερομηνία δεν αντιστοιχεί στο αναμενόμενο μοτίβο: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Η ημερομηνία είναι εκτός του αναμενόμενου εύρους ετών %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Εναλλαγή στη λειτουργία εισαγωγής ημερολογίου"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Εναλλαγή στη λειτουργία εισαγωγής κειμένου"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Κάντε κύλιση για εμφάνιση επόμενων ετών"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Κάντε κύλιση για εμφάνιση προηγούμενων ετών"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Επιλογή ημερομηνιών"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Ημερομηνία έναρξης"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Ημερομηνία λήξης"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Κάντε κύλιση για εμφάνιση του επόμενου μήνα"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Κάντε κύλιση για εμφάνιση του προηγούμενου μήνα"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Εντός εύρους"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Εισαγωγή ημερομηνιών"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Μη έγκυρη εισαγωγή εύρους ημερομηνιών"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Λαβή μεταφοράς"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Σύμπτυξη φύλλου κάτω μέρους"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Παράβλεψη φύλλου κάτω μέρους"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Ανάπτυξη φύλλου κάτω μέρους"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Επεξήγηση εργαλείου"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Προβολή επεξήγησης"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"ΜΜ"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"ΠΜ"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Επιλέξτε π.μ. ή μ.μ."</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Επιλογή ώρας"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Επιλογή λεπτών"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d η ώρα"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"Ώρα %1$d"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d λεπτά"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Λεπτό"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Ώρα"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"για λεπτά"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"για ώρα"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-en-rAU/strings.xml b/compose/material3/material3/src/androidMain/res/values-en-rAU/strings.xml
deleted file mode 100644
index 7eef271..0000000
--- a/compose/material3/material3/src/androidMain/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialogue"</string>
- <string name="expanded" msgid="5974471714631304645">"Expanded"</string>
- <string name="collapsed" msgid="5389587048670450460">"Collapsed"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Dismiss"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Search"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Suggestions below"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Select date"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Selected date"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Switch to selecting a year"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Swipe to select a year, or tap to switch back to selecting a day"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Change to next month"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Change to previous month"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Navigate to year %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Current selection: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"None"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Today"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Year picker visible"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Select date"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Entered date"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Date"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Entered date: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"None"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Date not allowed: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Date does not match expected pattern: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Date out of expected year range %1$s – %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Switch to calendar input mode"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Switch to text input mode"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Scroll to show later years"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Scroll to show earlier years"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Select dates"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Start date"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"End date"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Scroll to show the next month"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Scroll to show the previous month"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"In range"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Enter dates"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Invalid date range input"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Drag handle"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Collapse bottom sheet"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Dismiss bottom sheet"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Expand bottom sheet"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Tooltip"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Show tooltip"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Select a.m. or p.m."</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Select hour"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Select minutes"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d o\'clock"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d hours"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutes"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Hour"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"for minutes"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"for hour"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-en-rCA/strings.xml b/compose/material3/material3/src/androidMain/res/values-en-rCA/strings.xml
index 04a4e1d..e1286b7 100644
--- a/compose/material3/material3/src/androidMain/res/values-en-rCA/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-en-rCA/strings.xml
@@ -17,59 +17,112 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialog"</string>
- <string name="expanded" msgid="5974471714631304645">"Expanded"</string>
- <string name="collapsed" msgid="5389587048670450460">"Collapsed"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Dismiss"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Search"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Suggestions below"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Select date"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Selected date"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Switch to selecting a year"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Swipe to select a year, or tap to switch back to selecting a day"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Change to next month"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Change to previous month"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Navigate to year %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Current selection: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"None"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Today"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Year picker visible"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Select date"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Entered date"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Date"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Entered date: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"None"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Date not allowed: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Date does not match expected pattern: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Date out of expected year range %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Switch to calendar input mode"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Switch to text input mode"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Scroll to show later years"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Scroll to show earlier years"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Select dates"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Start date"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"End date"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Scroll to show the next month"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Scroll to show the previous month"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"In range"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Enter dates"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Invalid date range input"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Drag handle"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Collapse bottom sheet"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Dismiss bottom sheet"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Expand bottom sheet"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Tooltip"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Show tooltip"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Select AM or PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Select hour"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Select minutes"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d o\'clock"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d hours"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutes"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Hour"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"for minutes"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"for hour"</string>
+ <!-- no translation found for m3c_dialog (7617233117134790350) -->
+ <skip />
+ <string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Expanded"</string>
+ <string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Collapsed"</string>
+ <!-- no translation found for m3c_snackbar_dismiss (6152755701819882931) -->
+ <skip />
+ <!-- no translation found for m3c_search_bar_search (6152806324422087846) -->
+ <skip />
+ <!-- no translation found for m3c_suggestions_available (7655536806087401899) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_title (7430790972741451689) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_headline (7605002211875882969) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_switch_to_year_selection (791651718641787594) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_switch_to_day_selection (395627960681594326) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_switch_to_next_month (7142101321095356500) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_switch_to_previous_month (228438865139394590) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_navigate_to_year_description (8436650776581492840) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_headline_description (3664277305226978227) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_no_selection_description (5811000998184572395) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_today_description (3199387177749801575) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_year_picker_pane_title (2068382232816991922) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_title (7306227249789210568) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_headline (8166741421776570875) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_label (2895559812010326913) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_headline_description (229313757840775812) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_no_input_description (1237013946323089826) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_invalid_not_allowed (2521768508935305279) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_invalid_for_pattern (6116910750161463197) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_invalid_year_range (7052898923934555305) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_switch_to_calendar_mode (1804346892470238807) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_switch_to_input_mode (2219746470065162704) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_scroll_to_later_years (5727367015496556177) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_scroll_to_earlier_years (7813882352367152251) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_picker_title (3134165431120340385) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_picker_start_headline (4665981448952749820) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_picker_end_headline (4947636797751277713) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_picker_scroll_to_next_month (602077859540990149) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_picker_scroll_to_previous_month (4592174524846109496) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_picker_day_in_range (2138321128465719402) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_input_title (3148384720560189467) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_input_invalid_range_input (3190049423327661366) -->
+ <skip />
+ <!-- no translation found for m3c_bottom_sheet_drag_handle_description (8403354765404029791) -->
+ <skip />
+ <!-- no translation found for m3c_bottom_sheet_collapse_description (2988463736136100848) -->
+ <skip />
+ <!-- no translation found for m3c_bottom_sheet_dismiss_description (1555567894577437024) -->
+ <skip />
+ <!-- no translation found for m3c_bottom_sheet_expand_description (6670819569745899763) -->
+ <skip />
+ <!-- no translation found for m3c_tooltip_pane_description (5460405025248574620) -->
+ <skip />
+ <!-- no translation found for m3c_tooltip_long_press_label (1805687647081129904) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_pm (6616362054113087709) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_am (2786685010796619560) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_period_toggle_description (5865171949528594571) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_hour_selection (8876759303332837035) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_minute_selection (4699133535056739733) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_hour_suffix (3458167507790628988) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_hour_24h_suffix (9179527532316922345) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_minute_suffix (5064177921781937179) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_minute (4313071914266462005) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_hour (2349193472625211372) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_minute_text_field (7661234488295443182) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_hour_text_field (6973808109666874069) -->
+ <skip />
</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-en-rGB/strings.xml b/compose/material3/material3/src/androidMain/res/values-en-rGB/strings.xml
deleted file mode 100644
index 7eef271..0000000
--- a/compose/material3/material3/src/androidMain/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialogue"</string>
- <string name="expanded" msgid="5974471714631304645">"Expanded"</string>
- <string name="collapsed" msgid="5389587048670450460">"Collapsed"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Dismiss"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Search"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Suggestions below"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Select date"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Selected date"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Switch to selecting a year"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Swipe to select a year, or tap to switch back to selecting a day"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Change to next month"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Change to previous month"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Navigate to year %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Current selection: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"None"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Today"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Year picker visible"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Select date"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Entered date"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Date"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Entered date: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"None"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Date not allowed: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Date does not match expected pattern: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Date out of expected year range %1$s – %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Switch to calendar input mode"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Switch to text input mode"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Scroll to show later years"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Scroll to show earlier years"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Select dates"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Start date"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"End date"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Scroll to show the next month"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Scroll to show the previous month"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"In range"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Enter dates"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Invalid date range input"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Drag handle"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Collapse bottom sheet"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Dismiss bottom sheet"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Expand bottom sheet"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Tooltip"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Show tooltip"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Select a.m. or p.m."</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Select hour"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Select minutes"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d o\'clock"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d hours"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutes"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Hour"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"for minutes"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"for hour"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-en-rIN/strings.xml b/compose/material3/material3/src/androidMain/res/values-en-rIN/strings.xml
deleted file mode 100644
index 7eef271..0000000
--- a/compose/material3/material3/src/androidMain/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialogue"</string>
- <string name="expanded" msgid="5974471714631304645">"Expanded"</string>
- <string name="collapsed" msgid="5389587048670450460">"Collapsed"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Dismiss"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Search"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Suggestions below"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Select date"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Selected date"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Switch to selecting a year"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Swipe to select a year, or tap to switch back to selecting a day"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Change to next month"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Change to previous month"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Navigate to year %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Current selection: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"None"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Today"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Year picker visible"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Select date"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Entered date"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Date"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Entered date: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"None"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Date not allowed: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Date does not match expected pattern: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Date out of expected year range %1$s – %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Switch to calendar input mode"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Switch to text input mode"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Scroll to show later years"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Scroll to show earlier years"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Select dates"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Start date"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"End date"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Scroll to show the next month"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Scroll to show the previous month"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"In range"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Enter dates"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Invalid date range input"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Drag handle"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Collapse bottom sheet"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Dismiss bottom sheet"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Expand bottom sheet"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Tooltip"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Show tooltip"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Select a.m. or p.m."</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Select hour"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Select minutes"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d o\'clock"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d hours"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutes"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Hour"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"for minutes"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"for hour"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-en-rXC/strings.xml b/compose/material3/material3/src/androidMain/res/values-en-rXC/strings.xml
index dea2fdc..af84efe 100644
--- a/compose/material3/material3/src/androidMain/res/values-en-rXC/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-en-rXC/strings.xml
@@ -17,59 +17,112 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialog"</string>
- <string name="expanded" msgid="5974471714631304645">"Expanded"</string>
- <string name="collapsed" msgid="5389587048670450460">"Collapsed"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Dismiss"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Search"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Suggestions below"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Select date"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Selected date"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Switch to selecting a year"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Swipe to select a year, or tap to switch back to selecting a day"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Change to next month"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Change to previous month"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Navigate to year %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Current selection: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"None"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Today"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Year picker visible"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Select date"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Entered date"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Date"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Entered date: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"None"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Date not allowed: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Date does not match expected pattern: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Date out of expected year range %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Switch to calendar input mode"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Switch to text input mode"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Scroll to show later years"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Scroll to show earlier years"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Select dates"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Start date"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"End date"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Scroll to show the next month"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Scroll to show the previous month"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"In range"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Enter dates"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Invalid date range input"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Drag handle"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Collapse bottom sheet"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Dismiss bottom sheet"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Expand bottom sheet"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Tooltip"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Show tooltip"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Select AM or PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Select hour"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Select minutes"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d o\'clock"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d hours"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutes"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Hour"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"for minutes"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"for hour"</string>
+ <!-- no translation found for m3c_dialog (7617233117134790350) -->
+ <skip />
+ <string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Expanded"</string>
+ <string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Collapsed"</string>
+ <!-- no translation found for m3c_snackbar_dismiss (6152755701819882931) -->
+ <skip />
+ <!-- no translation found for m3c_search_bar_search (6152806324422087846) -->
+ <skip />
+ <!-- no translation found for m3c_suggestions_available (7655536806087401899) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_title (7430790972741451689) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_headline (7605002211875882969) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_switch_to_year_selection (791651718641787594) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_switch_to_day_selection (395627960681594326) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_switch_to_next_month (7142101321095356500) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_switch_to_previous_month (228438865139394590) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_navigate_to_year_description (8436650776581492840) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_headline_description (3664277305226978227) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_no_selection_description (5811000998184572395) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_today_description (3199387177749801575) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_year_picker_pane_title (2068382232816991922) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_title (7306227249789210568) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_headline (8166741421776570875) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_label (2895559812010326913) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_headline_description (229313757840775812) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_no_input_description (1237013946323089826) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_invalid_not_allowed (2521768508935305279) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_invalid_for_pattern (6116910750161463197) -->
+ <skip />
+ <!-- no translation found for m3c_date_input_invalid_year_range (7052898923934555305) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_switch_to_calendar_mode (1804346892470238807) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_switch_to_input_mode (2219746470065162704) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_scroll_to_later_years (5727367015496556177) -->
+ <skip />
+ <!-- no translation found for m3c_date_picker_scroll_to_earlier_years (7813882352367152251) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_picker_title (3134165431120340385) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_picker_start_headline (4665981448952749820) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_picker_end_headline (4947636797751277713) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_picker_scroll_to_next_month (602077859540990149) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_picker_scroll_to_previous_month (4592174524846109496) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_picker_day_in_range (2138321128465719402) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_input_title (3148384720560189467) -->
+ <skip />
+ <!-- no translation found for m3c_date_range_input_invalid_range_input (3190049423327661366) -->
+ <skip />
+ <!-- no translation found for m3c_bottom_sheet_drag_handle_description (8403354765404029791) -->
+ <skip />
+ <!-- no translation found for m3c_bottom_sheet_collapse_description (2988463736136100848) -->
+ <skip />
+ <!-- no translation found for m3c_bottom_sheet_dismiss_description (1555567894577437024) -->
+ <skip />
+ <!-- no translation found for m3c_bottom_sheet_expand_description (6670819569745899763) -->
+ <skip />
+ <!-- no translation found for m3c_tooltip_pane_description (5460405025248574620) -->
+ <skip />
+ <!-- no translation found for m3c_tooltip_long_press_label (1805687647081129904) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_pm (6616362054113087709) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_am (2786685010796619560) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_period_toggle_description (5865171949528594571) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_hour_selection (8876759303332837035) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_minute_selection (4699133535056739733) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_hour_suffix (3458167507790628988) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_hour_24h_suffix (9179527532316922345) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_minute_suffix (5064177921781937179) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_minute (4313071914266462005) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_hour (2349193472625211372) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_minute_text_field (7661234488295443182) -->
+ <skip />
+ <!-- no translation found for m3c_time_picker_hour_text_field (6973808109666874069) -->
+ <skip />
</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-es-rUS/strings.xml b/compose/material3/material3/src/androidMain/res/values-es-rUS/strings.xml
deleted file mode 100644
index db156dd..0000000
--- a/compose/material3/material3/src/androidMain/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Diálogo"</string>
- <string name="expanded" msgid="5974471714631304645">"Expandido"</string>
- <string name="collapsed" msgid="5389587048670450460">"Contraído"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Descartar"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Buscar"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Sugerencias a continuación"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Seleccionar fecha"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Fecha seleccionada"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Cambiar a seleccionar un año"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Desliza el dedo para elegir un año o presiona para volver a seleccionar un día"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Cambiar al mes siguiente"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Cambiar al mes anterior"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Navegar al año %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Selección actual: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Nada"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Hoy"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Selector de año visible"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Seleccionar fecha"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Fecha ingresada"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Fecha"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Fecha ingresada: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Ninguna"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Fecha no permitida: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"La fecha no coincide con el patrón esperado: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"La fecha está fuera del rango de años esperado: %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Cambiar al modo de entrada de calendario"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Cambiar al modo de entrada de texto"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Desplázate para ver los últimos años"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Desplázate para ver los primeros años"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Seleccionar fechas"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Fecha de inicio"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Fecha de finalización"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Desplázate para ver el próximo mes"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Desplázate para ver el mes anterior"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"En el rango"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Ingresar fechas"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Se introdujo un período no válido"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Controlador de arrastre"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Contraer la hoja inferior"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Descartar la hoja inferior"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Expandir la hoja inferior"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Información"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Mostrar información"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"p.m."</string>
- <string name="time_picker_am" msgid="5096144640014509074">"a.m."</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Selecciona a.m. o p.m."</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Seleccionar hora"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Seleccionar los minutos"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d en punto"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d horas"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutos"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minuto"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Hora"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"por minutos"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"por hora"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-es/strings.xml b/compose/material3/material3/src/androidMain/res/values-es/strings.xml
deleted file mode 100644
index 6226ef2..0000000
--- a/compose/material3/material3/src/androidMain/res/values-es/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Cuadro de diálogo"</string>
- <string name="expanded" msgid="5974471714631304645">"Desplegado"</string>
- <string name="collapsed" msgid="5389587048670450460">"Contraído"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Cerrar"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Buscar"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Sugerencias a continuación"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Seleccionar fecha"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Fecha seleccionada"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Cambiar para seleccionar un año"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Desliza el dedo para seleccionar un año o toca para volver a seleccionar un día"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Cambiar al mes siguiente"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Cambiar al mes anterior"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Ir al año %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Selección: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Ninguno"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Hoy"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Selector de año visible"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Seleccionar fecha"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Fecha introducida"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Fecha"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Fecha introducida: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Ninguna"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Fecha no permitida: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"La fecha no coincide con el patrón esperado: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Fecha fuera del intervalo de años previsto: %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Cambiar al modo de introducción de calendario"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Cambiar al modo de escritura"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Desplázate para ver los últimos años"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Desplázate para ver los años anteriores"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Seleccionar fechas"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Fecha de inicio"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Fecha de finalización"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Desplázate para ver el mes siguiente"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Desplázate para ver el mes anterior"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Dentro del intervalo"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Introducir fechas"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"El intervalo de fechas no es válido"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Controlador de arrastre"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Contrae la hoja inferior"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Cierra la hoja inferior"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Despliega la hoja inferior"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Descripción emergente"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Mostrar descripción emergente"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Selecciona AM o PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Seleccionar hora"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Seleccionar minutos"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d en punto"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d horas"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutos"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minutos"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Hora"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"por minutos"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"por hora"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-et/strings.xml b/compose/material3/material3/src/androidMain/res/values-et/strings.xml
deleted file mode 100644
index 85ea055..0000000
--- a/compose/material3/material3/src/androidMain/res/values-et/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialoog"</string>
- <string name="expanded" msgid="5974471714631304645">"Laiendatud"</string>
- <string name="collapsed" msgid="5389587048670450460">"Ahendatud"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Loobu"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Otsing"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Soovitused on allpool"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Valige kuupäev"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Valitud kuupäev"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Lülitu aasta valimisele"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Pühkige aasta valimiseks või puudutage, et minna tagasi päeva valimise juurde"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Vaheta järgmisele kuule"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Vaheta eelmisele kuule"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Liigu aasta %1$s juurde"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Praegune valik: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Pole"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Täna"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Aasta valija on nähtav"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Valige kuupäev"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Sisestatud kuupäev"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Kuupäev"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Sisestatud kuupäev: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Puudub"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Kuupäev pole lubatud: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Kuupäev ei ühti eeldatud mustriga: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Kuupäev on väljaspool eeldatud aastavahemikku %1$s–%2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Lülitu kalendrisisestusrežiimile"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Lülitu tekstisisestusrežiimile"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Hilisemate aastate kuvamiseks kerige"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Varasemate aastate kuvamiseks kerige"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Valige kuupäevad"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Alguskuupäev"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Lõppkuupäev"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Järgmise kuu kuvamiseks kerige"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Eelmise kuu kuvamiseks kerige"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Vahemikus"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Sisestage kuupäevad"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Sisestati sobimatu kuupäevavahemik"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Lohistamispide"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Alumise lehe ahendamine"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Alumisest lehest loobumine"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Alumise lehe laiendamine"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Kohtspikker"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Kuva kohtspikker"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Valige AM või PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Tunni valimine"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Minutite valimine"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d.00"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d tundi"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutit"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minutid"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Tunnid"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"minutite jaoks"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"tundide jaoks"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-eu/strings.xml b/compose/material3/material3/src/androidMain/res/values-eu/strings.xml
deleted file mode 100644
index 2f2f09d..0000000
--- a/compose/material3/material3/src/androidMain/res/values-eu/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Leihoa"</string>
- <string name="expanded" msgid="5974471714631304645">"Zabalduta"</string>
- <string name="collapsed" msgid="5389587048670450460">"Tolestuta"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Baztertu"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Bilaketa"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Iradokizunak daude behean"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Hautatu data bat"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Hautatutako data"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Joan urte-hautatzailera"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Pasatu hatza urte bat hautatzeko. Bestela, sakatu hau eguna hautatzeko pantailara itzultzeko."</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Aldatu hurrengo hilabetera"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Aldatu aurreko hilabetera"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Joan %1$s urtera"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Oraingo hautapena: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Bat ere ez"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Gaur"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Urte-hautatzailea ikusgai dago"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Hautatu data bat"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Idatzitako data"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Data"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Idatzitako data: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Bat ere ez"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Ez da onartzen data: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Data ez dator bat espero den ereduarekin: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Espero den urte tartetik (%1$s-%2$s) kanpo dago data"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Aldatu egutegiaren idazketa-metodora"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Aldatu testua idazteko modura"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Egin gora/behera etorkizuneko urteak erakusteko"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Egin gora/behera iraganeko urteak erakusteko"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Hautatu datak"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Hasiera-data"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Amaiera-data"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Egin gora/behera hurrengo hilabetea erakusteko"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Egin gora/behera aurreko hilabetea erakusteko"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Tartean"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Idatzi datak"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Idatzitako data tarteak ez du balio"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Arrastatzeko kontrol-puntua"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Tolestu pantailaren behealdean ainguratutako orria"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Baztertu pantailaren behealdean ainguratutako orria"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Zabaldu pantailaren behealdean ainguratutako orria"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Aholkua"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Erakutsi aholkua"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Hautatu AM edo PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Hautatu ordua"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Hautatu minutuak"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutu"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minutuak"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Orduak"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"minutuetarako"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ordurako"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-fa/strings.xml b/compose/material3/material3/src/androidMain/res/values-fa/strings.xml
deleted file mode 100644
index aaab237..0000000
--- a/compose/material3/material3/src/androidMain/res/values-fa/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"کادر گفتگو"</string>
- <string name="expanded" msgid="5974471714631304645">"ازهم بازشده"</string>
- <string name="collapsed" msgid="5389587048670450460">"جمعشده"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"بستن"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"جستجو"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"پیشنهادهای زیر"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"انتخاب تاریخ"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"تاریخ انتخابی"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"رفتن به انتخاب سال"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"برای انتخاب سال، تند بکشید یا برای برگشتن به انتخاب روز، ضربه بزنید"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"تغییر به ماه بعدی"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"تغییر به ماه قبلی"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"پیمایش به سال %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"انتخاب فعلی: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"هیچکدام"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"امروز"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"انتخابگر سال نمایان است"</string>
- <string name="date_input_title" msgid="3010396677286327048">"انتخاب تاریخ"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"تاریخ واردشده"</string>
- <string name="date_input_label" msgid="5194825853981987218">"تاریخ"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"تاریخ واردشده: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"هیچکدام"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"تاریخ مجاز نیست: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"تاریخ با الگوی موردانتظار مطابقت ندارد: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"تاریخ خارج از بازه زمانی %1$s تا %2$s است"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"رفتن به روش ورودی تقویم"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"رفتن به حالت ورودی نوشتاری"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"برای نمایش سالهای بعد پیمایش کنید"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"برای نمایش سالهای قبل پیمایش کنید"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"تاریخها را انتخاب کنید"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"تاریخ شروع"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"تاریخ پایان"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"برای نمایش ماه بعد پیمایش کنید"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"برای نمایش ماه قبل پیمایش کنید"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"در محدوده"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"تاریخها را وارد کنید"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"محدوده تاریخ واردشده نامعتبر است"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"دستگیره کشاندن"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"جمع کردن برگه زیرین"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"رد کردن برگه زیرین"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ازهم باز کردن برگه زیرین"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"نکتهابزار"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"نمایش نکتهابزار"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"ب.ظ."</string>
- <string name="time_picker_am" msgid="5096144640014509074">"ق.ظ."</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"انتخاب ق.ظ. یا ب.ظ."</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"انتخاب ساعت"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"انتخاب دقیقه"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"ساعت %1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d ساعت"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d دقیقه"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"دقیقه"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"ساعت"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"برای دقیقه"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"برای ساعت"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-fi/strings.xml b/compose/material3/material3/src/androidMain/res/values-fi/strings.xml
deleted file mode 100644
index f4fa38d..0000000
--- a/compose/material3/material3/src/androidMain/res/values-fi/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Valintaikkuna"</string>
- <string name="expanded" msgid="5974471714631304645">"Laajennettu"</string>
- <string name="collapsed" msgid="5389587048670450460">"Tiivistetty"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Hylkää"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Hae"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Ehdotuksia alla"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Valitse päivämäärä"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Valittu päivämäärä"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Vaihda vuoden valintaan"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Valitse vuosi pyyhkäisemällä tai palaa päivän valintaan napauttamalla"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Vaihda seuraavaan kuukauteen"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Vaihda edelliseen kuukauteen"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Siirry vuoteen %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Nykyinen valinta: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"–"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Tänään"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Vuosivalitsin näkyvillä"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Valitse päivämäärä"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Lisätty päivämäärä"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Päivämäärä"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Lisätty päivämäärä: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"–"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Päivämäärä ei sallittu: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Päivämäärä ei vastaa odotettua mallia: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Päivämäärä ei sisälly odotettuun vuosiaikaväliin: %1$s–%2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Vaihda syöttötavaksi kalenteri"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Vaihda tekstinsyöttötilaan"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Vieritä nähdäksesi myöhemmät vuodet"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Vieritä nähdäksesi aiemmat vuodet"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Valitse päivämäärät"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Alkamispäivä"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Päättymispäivä"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Vieritä nähdäksesi seuraavan kuukauden"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Vieritä nähdäksesi edellisen kuukauden"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Valitulla välillä"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Lisää päivämäärät"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Virheellinen ajanjakso"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Vetokahva"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Tiivistä alapaneeli"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Hylkää alapaneeli"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Laajenna alapaneeli"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Vihjeteksti"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Näytä vihjeteksti"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"IP"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AP"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Valitse AP tai IP"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Valitse tunti"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Valitse minuutit"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"Kello %1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d h"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minuuttia"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minuutti"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Tunti"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"minuuttien ajan"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"tunnin ajan"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-fr-rCA/strings.xml b/compose/material3/material3/src/androidMain/res/values-fr-rCA/strings.xml
deleted file mode 100644
index 61d15f1..0000000
--- a/compose/material3/material3/src/androidMain/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialogue"</string>
- <string name="expanded" msgid="5974471714631304645">"Développé"</string>
- <string name="collapsed" msgid="5389587048670450460">"Réduit"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Fermer"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Recherche"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Suggestions ci-dessous"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Sélectionnez une date"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Date sélectionnée"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Passer à la sélection d\'une année"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Balayez l\'écran pour sélectionner une année, ou touchez pour revenir en arrière et sélectionner un jour"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Passer au mois suivant"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Passer au mois précédent"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Naviguez jusqu\'à l\'année %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Sélection actuelle : %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Aucune"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Aujourd\'hui"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Sélecteur d\'année visible"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Sélectionnez une date"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Date entrée"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Date"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Date entrée : %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Aucune"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Date non autorisée : %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"La date ne correspond pas au schéma prévu : %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Date non comprise dans la fourchette prévue des années %1$s à %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Passer au mode d\'entrée de l\'Agenda"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Passer au mode d\'entrée de texte"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Faites défiler pour afficher les années suivantes"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Faites défiler pour afficher les années précédentes"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Sélectionner les dates"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Date de début"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Date de fin"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Faites défiler pour afficher le mois suivant"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Faites défiler pour afficher le mois précédent"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"À portée"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Entrer les dates"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Entrée de période incorrecte"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Poignée de déplacement"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Réduire la zone de contenu dans le bas de l\'écran"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Fermer la zone de contenu dans le bas de l\'écran"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Développer la zone de contenu dans le bas de l\'écran"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Infobulle"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Afficher une infobulle"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Sélectionner AM ou PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Sélectionner l\'heure"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Sélectionner les minutes"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d h"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d h"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutes"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Heure"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"pour les minutes"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"pour l\'heure"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-fr/strings.xml b/compose/material3/material3/src/androidMain/res/values-fr/strings.xml
deleted file mode 100644
index d0ec15d..0000000
--- a/compose/material3/material3/src/androidMain/res/values-fr/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Boîte de dialogue"</string>
- <string name="expanded" msgid="5974471714631304645">"Développé"</string>
- <string name="collapsed" msgid="5389587048670450460">"Réduit"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Ignorer"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Rechercher"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Suggestions ci-dessous"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Sélectionner une date"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Date sélectionnée"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Passer à la sélection d\'une année"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Balayez l\'écran pour sélectionner une année ou appuyez pour revenir à la sélection d\'un jour"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Passer au mois suivant"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Passer au mois précédent"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Accéder à l\'année %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Sélection actuelle : %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Aucune"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Aujourd\'hui"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Sélecteur d\'année visible"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Sélectionner une date"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Date saisie"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Date"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Date saisie : %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Aucune"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Date non autorisée : %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"La date ne correspond pas au format attendu : %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Date hors de la plage d\'années attendue : %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Passer au mode de saisie Agenda"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Passer au mode de saisie Texte"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Faites défiler pour afficher les années suivantes"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Faites défiler pour afficher les années précédentes"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Sélectionner des dates"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Date de début"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Date de fin"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Faites défiler pour afficher le mois suivant"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Faites défiler pour afficher le mois précédent"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Dans la plage"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Saisir des dates"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Plage de dates non valide"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Poignée de déplacement"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Réduire la bottom sheet"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Fermer la bottom sheet"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Développer la bottom sheet"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Info-bulle"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Afficher l\'info-bulle"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Sélectionner le format AM ou PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Sélectionner une heure"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Sélectionner des minutes"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d heures"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d heures"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutes"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Heure"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"en minutes"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"en heures"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-gl/strings.xml b/compose/material3/material3/src/androidMain/res/values-gl/strings.xml
deleted file mode 100644
index 0913a02..0000000
--- a/compose/material3/material3/src/androidMain/res/values-gl/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Cadro de diálogo"</string>
- <string name="expanded" msgid="5974471714631304645">"Despregado"</string>
- <string name="collapsed" msgid="5389587048670450460">"Contraído"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Pechar"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Busca"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Hai suxestións abaixo"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Selecciona a data"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Data seleccionada"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Cambiar a seleccionar un ano"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Pasa o dedo para seleccionar un ano ou toca a pantalla para volver á selección do día"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Cambiar ao mes seguinte"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Cambiar ao mes anterior"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Ir ao ano %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Selección actual: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Ningunha"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Hoxe"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Selector de ano visible"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Seleccionar data"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Data inserida"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Data"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Data inserida: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Ningunha"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Data non permitida: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"A data non coincide co padrón esperado: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"A data está fóra do intervalo de anos esperado (%1$s - %2$s)"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Cambiar ao modo de entrada de calendario"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Cambiar ao modo de introdución de texto"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Desprázate para mostrar anos posteriores"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Desprázate para mostrar anos anteriores"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Selecciona as datas"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Data de inicio"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Data de finalización"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Desprázate para mostrar o mes seguinte"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Desprázate para mostrar o mes anterior"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Dentro do intervalo"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Indica as datas"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Indicouse un intervalo de datas que non é válido"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Controlador de arrastre"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Contraer panel inferior"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Pechar panel inferior"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Despregar panel inferior"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Cadro de información"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Mostrar cadro de información"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"pm"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"am"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Seleccionar a. m. ou p. m."</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Seleccionar hora"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Seleccionar minutos"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d en pto."</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d horas"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutos"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minuto"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Hora"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"por minuto"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"por hora"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-gu/strings.xml b/compose/material3/material3/src/androidMain/res/values-gu/strings.xml
deleted file mode 100644
index b4a3309..0000000
--- a/compose/material3/material3/src/androidMain/res/values-gu/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"સંવાદ બૉક્સ"</string>
- <string name="expanded" msgid="5974471714631304645">"મોટી કરેલી"</string>
- <string name="collapsed" msgid="5389587048670450460">"નાની કરેલી"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"છોડી દો"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"શોધો"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"સૂચનો નીચે છે"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"તારીખ પસંદ કરો"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"પસંદ કરેલી તારીખ"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"વર્ષ પસંદ કરવાના વિકલ્પ પર સ્વિચ કરો"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"વર્ષ પસંદ કરવા માટે સ્વાઇપ કરો અથવા દિવસની પસંદગી પર પાછા સ્વિચ કરવા માટે ટૅપ કરો"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"બદલીને આગલો મહિનો પસંદ કરો"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"બદલીને પાછલો મહિનો પસંદ કરો"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"વર્ષ %1$s પર નૅવિગેટ કરો"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"હાલની પસંદગી: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"એકપણ નહીં"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"આજે"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"વર્ષ માટેનું પિકર દૃશ્યમાન છે"</string>
- <string name="date_input_title" msgid="3010396677286327048">"તારીખ પસંદ કરો"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"દાખલ કરેલી તારીખ"</string>
- <string name="date_input_label" msgid="5194825853981987218">"તારીખ"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"દાખલ કરેલી તારીખ: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"એકપણ નહીં"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"આ તારીખની મંજૂરી નથી: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"તારીખ અપેક્ષિત પૅટર્ન સાથે મેળ ખાતી નથી: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"અપેક્ષિત વર્ષની શ્રેણી %1$s - %2$sની બહારની તારીખ"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"કૅલેન્ડર ઇનપુટ મોડ પર સ્વિચ કરો"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"ટેક્સ્ટ ઇનપુટ મોડ પર સ્વિચ કરો"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"પછીના વર્ષો બતાવવા માટે સ્ક્રોલ કરો"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"અગાઉના વર્ષો બતાવવા માટે સ્ક્રોલ કરો"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"તારીખો પસંદ કરો"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"પ્રારંભ તારીખ"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"સમાપ્તિ તારીખ"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"આગલો મહિનો બતાવવા માટે સ્ક્રોલ કરો"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"પાછલો મહિનો બતાવવા માટે સ્ક્રોલ કરો"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"રેન્જમાં છે"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"તારીખો દાખલ કરો"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"તારીખની શ્રેણીનું અમાન્ય ઇનપુટ"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ઑબ્જેક્ટ ખેંચવાનું હૅન્ડલ"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"બોટમ શીટ નાની કરો"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"બોટમ શીટ છોડી દો"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"બોટમ શીટ મોટી કરો"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"ટૂલટિપ"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"ટૂલટિપ બતાવો"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"AM કે PM પસંદ કરો"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"કલાક પસંદ કરો"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"મિનિટ પસંદ કરો"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d વાગ્યે"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d કલાકે"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d મિનિટ"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"મિનિટ"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"કલાક"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"મિનિટ માટે"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"કલાક માટે"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-hi/strings.xml b/compose/material3/material3/src/androidMain/res/values-hi/strings.xml
deleted file mode 100644
index e0ad0f1..0000000
--- a/compose/material3/material3/src/androidMain/res/values-hi/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"डायलॉग"</string>
- <string name="expanded" msgid="5974471714631304645">"बड़ा किया गया"</string>
- <string name="collapsed" msgid="5389587048670450460">"छोटा किया गया"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"खारिज करें"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"खोजें"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"सुझाव यहां मौजूद हैं"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"तारीख चुनें"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"चुनी गई तारीख"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"साल चुनने के लिए स्विच करें"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"साल चुनने के लिए स्वाइप करें या दिन चुनने पर वापस स्विच करने लिए टैप करें"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"अगले महीने पर जाएं"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"पिछले महीने पर जाएं"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"साल %1$s पर जाएं"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"फ़िलहाल, यह चुना गया है: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"कोई नहीं"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"आज"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"साल चुनने का विकल्प दिख रहा है"</string>
- <string name="date_input_title" msgid="3010396677286327048">"तारीख चुनें"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"डाली गई तारीख"</string>
- <string name="date_input_label" msgid="5194825853981987218">"तारीख"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"डाली गई तारीख: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"कोई नहीं"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"यह तारीख सही नहीं है: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"तारीख सही फ़ॉर्मैट में नहीं डाली गई है: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"तारीख को साल के सही फ़ॉर्मैट में नहीं डाला गया है %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"कैलेंडर इनपुट मोड पर स्विच करें"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"टेक्स्ट इनपुट मोड पर स्विच करें"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"बाद वाले सालों की तारीख देखने के लिए स्क्रोल करें"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"पिछले सालों की तारीख देखने के लिए स्क्रोल करें"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"तारीखें चुनें"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"शुरू होने की तारीख"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"खत्म होने की तारीख"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"अगले महीने की तारीख देखने के लिए स्क्रोल करें"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"पिछले महीने की तारीख देखने के लिए स्क्रोल करें"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"रेंज में"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"तारीखें डालें"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"तारीख की दी गई सीमा गलत है"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"खींचकर छोड़ने वाला हैंडल"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"बॉटम शीट को छोटा करें"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"बॉटम शीट को खारिज करें"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"बॉटम शीट को बड़ा करें"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"टूलटिप"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"टूलटिप देखें"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"AM या PM चुनें"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"घंटा चुनें"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"मिनट चुनें"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d बजे"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d घंटे"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d मिनट"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"मिनट"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"घंटा"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"मिनट के लिए"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"घंटे के लिए"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-hr/strings.xml b/compose/material3/material3/src/androidMain/res/values-hr/strings.xml
deleted file mode 100644
index 12bf78f..0000000
--- a/compose/material3/material3/src/androidMain/res/values-hr/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dijaloški okvir"</string>
- <string name="expanded" msgid="5974471714631304645">"Prošireno"</string>
- <string name="collapsed" msgid="5389587048670450460">"Sažeto"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Odbaci"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Pretraživanje"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Prijedlozi su u nastavku"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Odaberite datum"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Odabrani datum"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Prijelaz na odabir godine"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Pomaknite se za odabir godine ili dodirnite za povratak na odabir dana"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Pomicanje na sljedeći mjesec"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Pomicanje na prethodni mjesec"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Prelazak u godinu %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Trenutačni odabir: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Ništa"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Danas"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Vidljiv je alat za odabir godine"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Odaberite datum"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Datum unosa"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Datum"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Datum unosa: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Ništa"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Datum nije dopušten: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Datum se ne podudara s očekivanim uzorkom: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Datum je izvan očekivanog raspona godine %1$s – %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Prijelaz na način unosa u Kalendaru"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Prijelaz na način unosa teksta"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Pomaknite se za prikaz kasnijih godina"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Pomaknite se za prikaz ranijih godina"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Odabir datuma"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Datum početka"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Datum završetka"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Pomaknite se za prikaz sljedećeg mjeseca"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Pomaknite se za prikaz prethodnog mjeseca"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"U dometu"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Unos datuma"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Unos datumskog raspona nije važeći"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Marker za povlačenje"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Sažimanje donje tablice"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Odbacivanje donje tablice"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Proširivanje donje tablice"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Opis"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Prikaži opis"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"Poslijepodne"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"Prijepodne"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Odaberite prijepodne ili poslijepodne"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Odabir sata"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Odabir minuta"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d h"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d h"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d min"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minuta"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Sat"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"minutama"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"na jedan sat"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-hu/strings.xml b/compose/material3/material3/src/androidMain/res/values-hu/strings.xml
deleted file mode 100644
index 9c8ccf6..0000000
--- a/compose/material3/material3/src/androidMain/res/values-hu/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Párbeszédablak"</string>
- <string name="expanded" msgid="5974471714631304645">"Kibontva"</string>
- <string name="collapsed" msgid="5389587048670450460">"Összecsukva"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Elvetés"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Keresés"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Javaslatok alább"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Dátum kiválasztása"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Kiválasztott dátum"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Váltson a kívánt év kiválasztásához"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Csúsztatással kiválaszthatja a kívánt évet, vagy koppintással visszaválthat a nap kiválasztásához."</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Váltás a következő hónapra"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Váltás az előző hónapra"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Navigálás a következő évhez: %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Jelenleg kiválasztva: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Nincs"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Ma"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Látható az évválasztó"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Dátum kiválasztása"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Megadott dátum"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Dátum"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Megadott dátum: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Nincs"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Nem engedélyezett dátum: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"A dátum nem felel meg a várt formátumnak: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"A dátum a várt időtartományon (%1$s – %2$s) kívül esik"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Váltás naptárbeviteli módra"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Váltás szövegbeviteli módra"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Görgessen a későbbi évek megjelenítéséhez"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Görgessen a korábbi évek megjelenítéséhez"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Válassza ki a kívánt dátumokat"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Kezdő dátum"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Befejezés dátuma"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Görgessen a következő hónap megjelenítéséhez"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Görgessen az előző hónap megjelenítéséhez"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Hatókörön belül"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Dátumok megadása"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Érvénytelen a megadott dátum"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Fogópont"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Az alsó lap összecsukása"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Az alsó lap elvetése"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Az alsó lap kibontása"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Elemleírás"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Elemleírás megjelenítése"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"du."</string>
- <string name="time_picker_am" msgid="5096144640014509074">"de."</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Válassza ki, hogy délelőtt vagy délután"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Óra kiválasztása"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Perc kiválasztása"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d óra"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d óra"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d perc"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Perc"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Óra"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"percre"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"órára"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-hy/strings.xml b/compose/material3/material3/src/androidMain/res/values-hy/strings.xml
deleted file mode 100644
index d964992..0000000
--- a/compose/material3/material3/src/androidMain/res/values-hy/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Երկխոսության պատուհան"</string>
- <string name="expanded" msgid="5974471714631304645">"Ծավալված է"</string>
- <string name="collapsed" msgid="5389587048670450460">"Ծալված է"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Փակել"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Որոնում"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Առաջարկները հասանելի են ստորև"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Ընտրեք ամսաթիվը"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Ընտրված ամսաթիվ"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Անցնել տարվա ընտրությանը"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Սահեցրեք՝ տարեթիվ ընտրելու համար, կամ հպեք՝ օրվա ընտրությանը վերադառնալու համար"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Անցնել հաջորդ ամսվան"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Անցնել նախորդ ամսվան"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Անցնել %1$s թվական"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Ընթացիկ ընտրությունը՝ %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Ոչ մեկը"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Այսօր"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Տարեթվի ցուցադրվող ընտրիչ"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Ընտրեք ամսաթիվը"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Մուտքագրված ամսաթիվ"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Ամսաթիվ"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Մուտքագրված ամսաթիվ՝ %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Ընտրված տարրեր չկան"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Ամսաթիվը թույլատրված չէ՝ %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Ամսաթիվը չի համընկնում թույլատրելի ձևաչափի հետ՝ %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Ամսաթիվը տարեթվերի թույլատրելի միջակայքից (%1$s – %2$s) դուրս է"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Անցնել օրացույցի մուտքագրման ռեժիմ"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Անցնել տեքստի մուտքագրման ռեժիմին"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Ոլորեք՝ վերջին տարիները ցուցադրելու համար"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Ոլորեք՝ նախորդ տարիները ցուցադրելու համար"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Ընտրեք ամսաթվեր"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Սկզբի ամսաթիվ"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Ավարտի ամսաթիվ"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Ոլորեք՝ հաջորդ ամիսը ցուցադրելու համար"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Ոլորեք՝ նախորդ ամիսը ցուցադրելու համար"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Միջակայքում"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Մուտքագրեք ամսաթվերը"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Մուտքագրված ամսաթվերի միջակայքն անվավեր է"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Տեղափոխման նշիչ"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Ծալել ներքևի էկրանը"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Փակել ներքևի էկրանը"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Ծավալել ներքևի էկրանը"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Հուշակ"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Ցուցադրել հուշում"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Ընտրել AM կամ PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Ընտրել ժամը"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Ընտրել րոպեն"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d ժամ"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d րոպե"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Րոպե"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Ժամ"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"րոպեներ"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ժամեր"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-in/strings.xml b/compose/material3/material3/src/androidMain/res/values-in/strings.xml
deleted file mode 100644
index 36ba535..0000000
--- a/compose/material3/material3/src/androidMain/res/values-in/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialog"</string>
- <string name="expanded" msgid="5974471714631304645">"Diluaskan"</string>
- <string name="collapsed" msgid="5389587048670450460">"Diciutkan"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Tutup"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Telusuri"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Saran di bawah"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Pilih tanggal"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Tanggal yang dipilih"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Beralih ke memilih tahun"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Geser untuk memilih tahun, atau ketuk untuk beralih kembali ke pemilihan tanggal"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Ubah ke bulan berikutnya"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Ubah ke bulan sebelumnya"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Pilih tahun %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Pilihan saat ini: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Tidak ada"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Hari ini"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Pemilih tahun terlihat"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Pilih tanggal"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Tanggal yang dimasukkan"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Tanggal"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Tanggal yang dimasukkan: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Tidak ada"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Tanggal tidak diizinkan: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Tanggal tidak cocok dengan pola yang diharapkan: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Tanggal di luar rentang tahun yang diharapkan %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Beralih ke mode input kalender"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Beralih ke mode input teks"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Scroll untuk menampilkan tahun berikutnya"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Scroll untuk menampilkan tahun sebelumnya"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Pilih tanggal"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Tanggal mulai"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Tanggal akhir"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Scroll untuk menampilkan bulan berikutnya"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Scroll untuk menampilkan bulan sebelumnya"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Dalam rentang"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Masukkan tanggal"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Input rentang tanggal tidak valid"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Handel geser"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Menciutkan sheet bawah"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Menutup sheet bawah"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Meluaskan sheet bawah"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Tooltip"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Tampilkan tooltip"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Pilih AM atau PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Pilih jam"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Pilih menit"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"Pukul %1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d jam"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d menit"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Menit"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Jam"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"untuk menit"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"untuk jam"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-is/strings.xml b/compose/material3/material3/src/androidMain/res/values-is/strings.xml
deleted file mode 100644
index ff5ec27..0000000
--- a/compose/material3/material3/src/androidMain/res/values-is/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Gluggi"</string>
- <string name="expanded" msgid="5974471714631304645">"Stækkað"</string>
- <string name="collapsed" msgid="5389587048670450460">"Minnkað"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Hunsa"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Leit"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Tillögur hér fyrir neðan"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Velja dagsetningu"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Valin dagsetning"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Skipta yfir í val á ári"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Strjúktu til að velja ár eða ýttu til að skipta aftur yfir í að velja dag"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Breyta í næsta mánuð"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Breyta í fyrri mánuð"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Fletta til ársins %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Núverandi val: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Ekkert"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Í dag"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Ársval birt"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Velja dagsetningu"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Skráð dagsetning"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Dagsetning"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Skráð dagsetning: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Ekkert"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Dagsetning er ekki leyfileg: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Dagsetning passar ekki við áætlað mynstur: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Dagsetning er utan áætlaðra ára: %1$s–%2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Skipta yfir í innfærsluaðferð fyrir dagatal"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Skipta yfir í textainnslátt"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Flettu til að sjá síðari ár"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Flettu til að sjá fyrri ár"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Veldu dagsetningar"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Upphafsdagur"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Lokadagur"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Flettu til að sjá næsta mánuð"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Flettu til að sjá fyrri mánuð"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Innan tímabils"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Sláðu inn dagsetningar"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Ógilt tímabil fært inn"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Dragkló"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Minnka blað neðst"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Hunsa blað neðst"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Stækka blað neðst"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Ábending"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Sýna ábendingu"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"eh"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"fh"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Velja f.h. eða e.h."</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Velja klst."</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Velja mínútur"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"Kl. %1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d klst."</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d mínútur"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Mínúta"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Klukkustund"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"fyrir mínútur"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"fyrir klukkustund"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-it/strings.xml b/compose/material3/material3/src/androidMain/res/values-it/strings.xml
deleted file mode 100644
index 6959ff1..0000000
--- a/compose/material3/material3/src/androidMain/res/values-it/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Finestra di dialogo"</string>
- <string name="expanded" msgid="5974471714631304645">"Controllo espanso"</string>
- <string name="collapsed" msgid="5389587048670450460">"Controllo compresso"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Chiudi"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Cerca"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Suggerimenti sotto"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Seleziona data"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Data selezionata"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Passa alla selezione di un anno"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Scorri per selezionare un anno o tocca per tornare alla selezione di un giorno"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Passa al mese successivo"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Passa al mese precedente"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Vai all\'anno %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Selezione attuale: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Nessuna selezione"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Oggi"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Selettore dell\'anno visibile"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Seleziona data"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Data inserita"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Data"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Data inserita: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Nessuna"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Data non consentita: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"La data non corrisponde al pattern previsto: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"La data non rientra nell\'intervallo di anni previsto (%1$s-%2$s)"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Passa alla modalità di immissione Calendario"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Passa alla modalità di immissione Testo"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Scorri per visualizzare gli anni successivi"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Scorri per visualizzare gli anni precedenti"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Seleziona date"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Data di inizio"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Data di fine"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Scorri per visualizzare il mese successivo"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Scorri per visualizzare il mese precedente"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Nell\'intervallo"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Inserisci date"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Intervallo di date inserito non valido"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Punto di trascinamento"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Comprimi il riquadro inferiore"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Chiudi il riquadro inferiore"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Espandi il riquadro inferiore"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Descrizione comando"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Mostra descrizione comando"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Seleziona AM o PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Seleziona ora"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Seleziona i minuti"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d ore"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minuti"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minuto"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Ora"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"per minuti"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"per ora"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-iw/strings.xml b/compose/material3/material3/src/androidMain/res/values-iw/strings.xml
deleted file mode 100644
index a0750c6..0000000
--- a/compose/material3/material3/src/androidMain/res/values-iw/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"תיבת דו-שיח"</string>
- <string name="expanded" msgid="5974471714631304645">"מורחב"</string>
- <string name="collapsed" msgid="5389587048670450460">"מכווץ"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"סגירה"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"חיפוש"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"הצעות מופיעות למטה"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"בחירת תאריך"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"התאריך הנבחר"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"החלפה לבחירה של שנה"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"יש להחליק כדי לבחור שנה, או להקיש כדי לחזור לבחירת היום"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"מעבר לחודש הבא"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"מעבר לחודש הקודם"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"ניווט לשנת %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"הבחירה הנוכחית: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"ללא"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"היום"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"בורר השנה גלוי"</string>
- <string name="date_input_title" msgid="3010396677286327048">"בחירת תאריך"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"התאריך שהוזן"</string>
- <string name="date_input_label" msgid="5194825853981987218">"תאריך"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"התאריך שהוזן: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"ללא"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"תאריך לא מורשה: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"התאריך לא תואם לקו ביטול הנעילה הצפוי: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"התאריך נמצא מחוץ לטווח השנים הצפוי %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"מעבר לשיטת קלט של יומן"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"מעבר לשיטת קלט של טקסט"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"צריך לגלול כדי להציג את השנים המאוחרות"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"צריך לגלול כדי להציג את השנים הקודמות"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"בחירת תאריכים"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"תאריך התחלה"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"תאריך סיום"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"צריך לגלול כדי להציג את החודש הבא"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"צריך לגלול כדי להציג את החודש הקודם"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"בטווח"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"הזנת תאריכים"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"קלט טווח תאריכים לא חוקי"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"נקודת אחיזה לגרירה"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"כיווץ הגיליון התחתון"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"סגירת הגיליון התחתון"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"הרחבת הגיליון התחתון"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"הסבר קצר"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"הצגת הסבר קצר"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"צריך לבחור ב-AM או ב-PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"בחירת שעה"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"בחירת דקות"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d שעות"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d דקות"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"דקות"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"שעות"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"דקות"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"שעות"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ja/strings.xml b/compose/material3/material3/src/androidMain/res/values-ja/strings.xml
deleted file mode 100644
index 3ff0d0c..0000000
--- a/compose/material3/material3/src/androidMain/res/values-ja/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"ダイアログ"</string>
- <string name="expanded" msgid="5974471714631304645">"開いています"</string>
- <string name="collapsed" msgid="5389587048670450460">"閉じています"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"閉じる"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"検索"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"検索候補は次のとおりです"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"日付を選択します"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"選択した日付"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"年の選択に移行"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"スワイプして年を選択するか、タップして日付の選択に戻ります"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"翌月に変更"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"前月に変更"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"年に移動 %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"現在の選択: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"なし"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"今日"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"年の選択ツールの表示"</string>
- <string name="date_input_title" msgid="3010396677286327048">"日付を選択"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"入力された日付"</string>
- <string name="date_input_label" msgid="5194825853981987218">"日付"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"入力された日付: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"なし"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"許可されていない日付です: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"想定されるパターンと日付が一致しません: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"想定される年の範囲(%1$s~%2$s)から日付が外れています"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"カレンダー入力モードに切り替え"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"テキスト入力モードに切り替え"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"これより後の年を表示するにはスクロールしてください"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"これより前の年を表示するにはスクロールしてください"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"日付の選択"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"開始日"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"終了日"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"次の月を表示するにはスクロールしてください"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"前の月を表示するにはスクロールしてください"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"範囲内"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"日付の入力"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"入力された期間は無効です"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ドラッグ ハンドル"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"ボトムシートを折りたたみます"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ボトムシートを閉じます"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ボトムシートを開きます"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"ツールチップ"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"ツールチップを表示します"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"午前または午後を選択"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"時刻を選択"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"分を選択"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d 時"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d 時間"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d 分"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"分"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"時間"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"(分単位)"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"(時間単位)"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ka/strings.xml b/compose/material3/material3/src/androidMain/res/values-ka/strings.xml
deleted file mode 100644
index 0a0e223..0000000
--- a/compose/material3/material3/src/androidMain/res/values-ka/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"დიალოგი"</string>
- <string name="expanded" msgid="5974471714631304645">"გაფართოებულია"</string>
- <string name="collapsed" msgid="5389587048670450460">"ჩაკეცილი"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"დახურვა"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"ძიება"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"შემოთავაზებები იხილეთ ქვემოთ"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"აირჩიეთ თარიღი"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"არჩეული თარიღი"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"წლის არჩევაზე გადასვლა"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"გადაფურცლეთ წლის ასარჩევად, ან შეხებით აირჩიეთ ისევ დღის არჩევაზე გადართვა"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"შემდეგ თვეზე გადასვლა"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"წინა თვეზე გადასვლა"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"%1$s-ზე გადასვლა"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"ამჟამინდელი არჩევანი: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"არცერთი"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"დღეს"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"არჩეული წელი ხილულია"</string>
- <string name="date_input_title" msgid="3010396677286327048">"აირჩიეთ თარიღი"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"შეყვანილი სახელი"</string>
- <string name="date_input_label" msgid="5194825853981987218">"თარიღი"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"შეყვანილი თარიღი: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"არცერთი"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"თარიღი დაუშვებელია: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"თარიღი არ ემთხვევა მოსალოდნელ ნიმუშს: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"თარიღი არ არის წლების მოსალოდნელ დიაპაზონში %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"კალენდარში შეყვანის რეჟიმზე გადართვა"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"ტექსტის შეყვანის რეჟიმზე გადართვა"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"გადააადგილეთ შემდგომი წლების საჩვენებლად"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"გადააადგილეთ წინა წლების საჩვენებლად"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"თარიღების არჩევა"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"დაწყების თარიღი"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"დასრულების თარიღი"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"გადააადგილეთ შემდეგი თვის საჩვენებლად"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"გადააადგილეთ წინა თვის საჩვენებლად"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"არეალშია"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"თარიღების შეყვანა"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"შეყვანილია თარიღების არასწორი დიაპაზონი"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"სახელური ჩავლებისთვის"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"ქვედა ფურცლის ჩაკეცვა"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ქვედა ფურცლის უარყოფა"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ქვედა ფურცლის გაშლა"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"მინიშნება"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"მინიშნების ჩვენება"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"აირჩიეთ AM ან PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"აირჩიეთ საათი"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"აირჩიეთ წუთები"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d სთ"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d საათი"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d წთ"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"წუთი"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"საათი"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"რამდენიმე წუთით"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ერთი საათით"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-kk/strings.xml b/compose/material3/material3/src/androidMain/res/values-kk/strings.xml
deleted file mode 100644
index 7ba95aa..0000000
--- a/compose/material3/material3/src/androidMain/res/values-kk/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Диалогтік терезе"</string>
- <string name="expanded" msgid="5974471714631304645">"Жайылды"</string>
- <string name="collapsed" msgid="5389587048670450460">"Жиылды"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Жабу"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Іздеу"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Төмендегі ұсыныстар"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Күн таңдау"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Таңдалған күн"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Жыл таңдауға өту"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Жыл таңдау үшін сырғытыңыз. Күн таңдауға ауысу үшін түртіңіз."</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Келесі айға өзгерту"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Алдыңғы айға өзгерту"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Мына жылға өту: %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Қазіргі таңдау: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Ешқандай"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Бүгін"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Көрсетілген жыл таңдағышы"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Күнді таңдаңыз"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Деректер енгізілді"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Күні"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Деректер енгізілді: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Жоқ"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Деректер рұқсат етілмейді: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Деректер болжалды өрнекке сай келмейді: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Күтілетін жыл аралығы: %1$s–%2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Күнтізбенің енгізу режиміне ауысу"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Мәтін енгізу режиміне ауысу"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Кейінгі жылдарды көрсету үшін айналдырыңыз."</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Алдыңғы жылдарды көрсету үшін айналдырыңыз."</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Күндер таңдау"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Басталу күні"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Аяқталу күні"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Келесі айды көрсету үшін айналдырыңыз."</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Алдыңғы айды көрсету үшін айналдырыңыз."</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Күндер аралығында"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Күндерді енгізіңіз"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Жарамсыз күндер аралығы енгізілген."</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Сүйрейтін тетік"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Төменгі парақшаны жию"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Төменгі парақшаны жабу"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Төменгі парақшаны жаю"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Қалқыма көмек"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Қалқыма көмекті көрсету"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"түстен кейін"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"түске дейін"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"\"AM\" немесе \"PM\" форматын таңдау"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Сағатты таңдау"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Минут таңдау"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d сағат"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d сағат"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d минут"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Mинут"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Сағат"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"минут"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"сағат"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-km/strings.xml b/compose/material3/material3/src/androidMain/res/values-km/strings.xml
deleted file mode 100644
index 3a087c7..0000000
--- a/compose/material3/material3/src/androidMain/res/values-km/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"ប្រអប់"</string>
- <string name="expanded" msgid="5974471714631304645">"បានពង្រីក"</string>
- <string name="collapsed" msgid="5389587048670450460">"បានបង្រួម"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"ច្រានចោល"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"ស្វែងរក"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"ការណែនាំខាងក្រោម"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"ជ្រើសរើសកាលបរិច្ឆេទ"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"កាលបរិច្ឆេទដែលបានជ្រើសរើស"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"ប្ដូរទៅការជ្រើសរើសឆ្នាំ"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"អូសដើម្បីជ្រើសរើសឆ្នាំ ឬចុចដើម្បីប្ដូរត្រឡប់ទៅការជ្រើសរើសថ្ងៃវិញ"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"ប្ដូរទៅខែបន្ទាប់"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"ប្ដូរទៅខែមុន"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"រុករកទៅកាន់ឆ្នាំ %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"ការជ្រើសរើសបច្ចុប្បន្ន៖ %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"គ្មាន"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"ថ្ងៃនេះ"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"អាចមើលឃើញផ្ទាំងជ្រើសរើសឆ្នាំ"</string>
- <string name="date_input_title" msgid="3010396677286327048">"ជ្រើសរើសកាលបរិច្ឆេទ"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"កាលបរិច្ឆេទដែលបានបញ្ចូល"</string>
- <string name="date_input_label" msgid="5194825853981987218">"កាលបរិច្ឆេទ"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"កាលបរិច្ឆេទដែលបានបញ្ចូល៖ %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"គ្មាន"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"កាលបរិច្ឆេទដែលមិនបានអនុញ្ញាត៖ %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"កាលបរិច្ឆេទមិនត្រូវគ្នានឹងលំនាំដែលរំពឹងទុកទេ៖ %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"កាលបរិច្ឆេទដែលស្ថិតនៅក្រៅចន្លោះឆ្នាំដែលរំពឹងទុក %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"ប្ដូរទៅមុខងារបញ្ចូលប្រតិទិន"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"ប្ដូរទៅមុខងារបញ្ចូលអក្សរ"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"រំកិលដើម្បីបង្ហាញឆ្នាំក្រោយៗ"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"រំកិលដើម្បីបង្ហាញឆ្នាំមុនៗ"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"ជ្រើសរើសកាលបរិច្ឆេទ"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"កាលបរិច្ឆេទចាប់ផ្ដើម"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"កាលបរិច្ឆេទបញ្ចប់"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"រំកិលដើម្បីបង្ហាញខែក្រោយ"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"រំកិលដើម្បីបង្ហាញខែមុន"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"ក្នុងចន្លោះ"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"បញ្ចូលកាលបរិច្ឆេទ"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"ការបញ្ចូលចន្លោះកាលបរិច្ឆេទមិនត្រឹមត្រូវ"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ដងអូស"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"បង្រួមសន្លឹកខាងក្រោម"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ច្រានចោលសន្លឹកខាងក្រោម"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ពង្រីកសន្លឹកខាងក្រោម"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"កំណត់ពន្យល់"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"បង្ហាញកំណត់ពន្យល់"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"ជ្រើសរើស AM ឬ PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"ជ្រើសរើសម៉ោង"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"ជ្រើសនាទី"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"ម៉ោង %1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d ម៉ោង"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d នាទី"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"នាទី"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"ម៉ោង"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"រយៈពេលប៉ុន្មាននាទី"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"រយៈពេលប៉ុន្មានម៉ោង"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-kn/strings.xml b/compose/material3/material3/src/androidMain/res/values-kn/strings.xml
deleted file mode 100644
index 7e4f82e..0000000
--- a/compose/material3/material3/src/androidMain/res/values-kn/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"ಡೈಲಾಗ್"</string>
- <string name="expanded" msgid="5974471714631304645">"ವಿಸ್ತರಿಸಲಾಗಿದೆ"</string>
- <string name="collapsed" msgid="5389587048670450460">"ಕುಗ್ಗಿಸಲಾಗಿದೆ"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"ವಜಾಗೊಳಿಸಿ"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"ಹುಡುಕಿ"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"ಸಲಹೆಗಳನ್ನು ಕೆಳಗೆ ನೀಡಲಾಗಿದೆ"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"ದಿನಾಂಕವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"ದಿನಾಂಕವನ್ನು ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"ವರ್ಷವನ್ನು ಆಯ್ಕೆ ಮಾಡಲು ಬದಲಿಸಿ"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"ಒಂದು ವರ್ಷವನ್ನು ಆಯ್ಕೆಮಾಡಲು ಸ್ವೈಪ್ ಮಾಡಿ ಅಥವಾ ಒಂದು ದಿನವನ್ನು ಆಯ್ಕೆಮಾಡಲು ಹಿಂತಿರುಗಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"ಮುಂದಿನ ತಿಂಗಳಿಗೆ ಬದಲಿಸಿ"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"ಹಿಂದಿನ ತಿಂಗಳಿಗೆ ಬದಲಿಸಿ"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"%1$s ವರ್ಷಕ್ಕೆ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"ಪ್ರಸ್ತುತ ಆಯ್ಕೆ: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"ಯಾವುದೂ ಅಲ್ಲ"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"ಇಂದು"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"ವರ್ಷದ ಪಿಕರ್ ಗೋಚರಿಸುತ್ತದೆ"</string>
- <string name="date_input_title" msgid="3010396677286327048">"ದಿನಾಂಕವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"ನಮೂದಿಸಿದ ದಿನಾಂಕ"</string>
- <string name="date_input_label" msgid="5194825853981987218">"ದಿನಾಂಕ"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"ನಮೂದಿಸಿದ ದಿನಾಂಕ: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"ಯಾವುದೂ ಅಲ್ಲ"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"ದಿನಾಂಕವನ್ನು ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"ನಿರೀಕ್ಷಿಸಿದ ಪ್ಯಾಟರ್ನ್ನೊಂದಿಗೆ ದಿನಾಂಕ ಹೊಂದಾಣಿಕೆಯಾಗುತ್ತಿಲ್ಲ: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"ದಿನಾಂಕವು ನಿರೀಕ್ಷಿಸಿದ ವರ್ಷದ ವ್ಯಾಪ್ತಿಯನ್ನು ಮೀರಿದೆ %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"ಕ್ಯಾಲೆಂಡರ್ ಇನ್ಪುಟ್ ಮೋಡ್ಗೆ ಬದಲಿಸಿ"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"ಪಠ್ಯ ಇನ್ಪುಟ್ ಮೋಡ್ಗೆ ಬದಲಿಸಿ"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"ನಂತರದ ವರ್ಷಗಳನ್ನು ತೋರಿಸಲು ಸ್ಕ್ರಾಲ್ ಮಾಡಿ"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"ಹಿಂದಿನ ವರ್ಷಗಳನ್ನು ತೋರಿಸಲು ಸ್ಕ್ರಾಲ್ ಮಾಡಿ"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"ದಿನಾಂಕಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"ಆರಂಭ ದಿನಾಂಕ"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"ಅಂತಿಮ ದಿನಾಂಕ"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"ಮುಂದಿನ ತಿಂಗಳನ್ನು ತೋರಿಸಲು ಸ್ಕ್ರಾಲ್ ಮಾಡಿ"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"ಹಿಂದಿನ ತಿಂಗಳನ್ನು ತೋರಿಸಲು ಸ್ಕ್ರಾಲ್ ಮಾಡಿ"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"ವ್ಯಾಪ್ತಿಯಲ್ಲಿದೆ"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"ದಿನಾಂಕಗಳನ್ನು ನಮೂದಿಸಿ"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"ದಿನಾಂಕ ವ್ಯಾಪ್ತಿಯ ಇನ್ಪುಟ್ ಅಮಾನ್ಯವಾಗಿದೆ"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ಹ್ಯಾಂಡಲ್ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"ಕೆಳಭಾಗದ ಶೀಟ್ ಅನ್ನು ಕುಗ್ಗಿಸಿ"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ಕೆಳಭಾಗದ ಶೀಟ್ ಅನ್ನು ವಜಾಗೊಳಿಸಿ"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ಕೆಳಭಾಗದ ಶೀಟ್ ಅನ್ನು ವಿಸ್ತರಿಸಿ"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"ಟೂಲ್ಟಿಪ್"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"ಟೂಲ್ಟಿಪ್ ಅನ್ನು ತೋರಿಸಿ"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"AM ಅಥವಾ PM ಆಯ್ಕೆಮಾಡಿ"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"ಸಮಯವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"ನಿಮಿಷಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d ಓ ಕ್ಲಾಕ್"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d ಗಂಟೆ"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d ನಿಮಿಷಗಳು"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"ನಿಮಿಷ"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"ಗಂಟೆ"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"ನಿಮಿಷಗಳವರೆಗೆ"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ಗಂಟೆಯವರೆಗೆ"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ko/strings.xml b/compose/material3/material3/src/androidMain/res/values-ko/strings.xml
deleted file mode 100644
index 67735f6..0000000
--- a/compose/material3/material3/src/androidMain/res/values-ko/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"대화상자"</string>
- <string name="expanded" msgid="5974471714631304645">"펼침"</string>
- <string name="collapsed" msgid="5389587048670450460">"접힘"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"닫기"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"검색"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"아래의 추천 검색어"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"날짜 선택"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"선택한 날짜"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"연도 선택으로 전환"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"스와이프하여 연도를 선택하거나 탭하여 날짜 선택으로 돌아가세요."</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"다음 달로 변경"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"이전 달로 변경"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"%1$s년으로 이동"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"현재 선택사항: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"없음"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"오늘"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"연도 선택 도구 표시"</string>
- <string name="date_input_title" msgid="3010396677286327048">"날짜 선택"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"입력한 날짜"</string>
- <string name="date_input_label" msgid="5194825853981987218">"날짜"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"입력한 날짜: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"없음"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"데이터 허용 안 됨: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"데이터가 예상 패턴과 일치하지 않음: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"데이터가 예상 연도 범위(%1$s~%2$s)를 벗어남"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"캘린더 입력 모드로 전환"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"텍스트 입력 모드로 전환"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"스크롤하여 이후 연도 보기"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"스크롤하여 이전 연도 보기"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"날짜 선택"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"시작일"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"종료일"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"스크롤하여 다음 달 보기"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"스크롤하여 이전 달 보기"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"범위 내"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"날짜 입력"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"잘못된 기간 입력"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"드래그 핸들"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"하단 시트 접기"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"하단 시트 닫기"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"하단 시트 펼치기"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"도움말"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"도움말 표시"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"오후"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"오전"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"오전 또는 오후를 선택하세요."</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"시간 선택"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"분 선택"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d시 정각"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d시간"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d분"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"분"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"시간"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"기간(분)"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"기간(시간)"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ky/strings.xml b/compose/material3/material3/src/androidMain/res/values-ky/strings.xml
deleted file mode 100644
index e4d6c16..0000000
--- a/compose/material3/material3/src/androidMain/res/values-ky/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Диалог"</string>
- <string name="expanded" msgid="5974471714631304645">"Жайылып көрсөтүлдү"</string>
- <string name="collapsed" msgid="5389587048670450460">"Жыйыштырылды"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Жабуу"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Издөө"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Сунуштар төмөндө келтирилди"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Күндү тандоо"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Тандалган күн"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Жыл тандоого которулуу"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Жылды тандоо үчүн экранды сүрүңүз же күндү тандоого кайтуу үчүн таптап коюңуз"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Кийинки айга өзгөртүү"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Мурунку айга өзгөртүү"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"%1$s-жылга өтүү"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Учурда %1$s тандалды"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Жок"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Бүгүн"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Көрсөтүлгөн жыл тандагыч"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Күндү тандоо"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Киргизилген күн"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Күнү"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Киргизилген күн: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Жок"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Күндүн мындай форматын колдонууга болбойт: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Күндүн форматы үлгүгө дал келген жок: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Күн %1$s — %2$s деп белгиленген жылдар диапазонуна кирбей калды"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Жылнаамага киргизүү режимине которулуу"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Текст киргизүү режимине которулуу"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Кийинки жылдарды көрүү үчүн сыдырыңыз"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Мурунку жылдарды көрүү үчүн сыдырыңыз"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Күндөрдү тандоо"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Башталуу күнү"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Аяктоо күнү"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Кийинки айды көрүү үчүн сыдырыңыз"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Мурунку айды көрүү үчүн сыдырыңыз"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Төмөнкү убакыт аралыгындагы күн"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Күндөрдү киргизүү"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Даталар диапазону туура эмес тандалды"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Тизменин керектүү жерине сүйрөп баруу"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Ылдыйкы экранды жыйыштыруу"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Ылдыйкы экранды жабуу"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Ылдыйкы экранды жайып көрсөтүү"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Калкып чыгуучу кеңеш"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Калкып чыгуучу кеңешти көрсөтүү"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"түштөн кийин"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"түшкө чейин"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Түшкө чейинки же түштөн кийинки убакытты тандоо"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Саат тандоо"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Мүнөттөрдү тандоо"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d саат"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d саат"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d мүнөт"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Мүнөт"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Саат"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"мүнөткө"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"саатка"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-lo/strings.xml b/compose/material3/material3/src/androidMain/res/values-lo/strings.xml
deleted file mode 100644
index c3cc53a..0000000
--- a/compose/material3/material3/src/androidMain/res/values-lo/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"ກ່ອງໂຕ້ຕອບ"</string>
- <string name="expanded" msgid="5974471714631304645">"ຂະຫຍາຍແລ້ວ"</string>
- <string name="collapsed" msgid="5389587048670450460">"ຫຍໍ້ແລ້ວ"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"ປິດໄວ້"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"ຊອກຫາ"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"ການແນະນຳຢູ່ຂ້າງລຸ່ມ"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"ເລືອກວັນທີ"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"ວັນທີທີ່ເລືອກໄວ້"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"ປ່ຽນໄປເລືອກປີ"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"ປັດເພື່ອເລືອກປີ ຫຼື ແຕະເພື່ອສະຫຼັບກັບໄປຫາການເລືອກວັນ"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"ປ່ຽນເປັນເດືອນຕໍ່ໄປ"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"ປ່ຽນເປັນເດືອນຜ່ານມາ"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"ນຳທາງໄປຫາປີ %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"ການເລືອກປັດຈຸບັນ: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"ບໍ່ມີ"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"ມື້ນີ້"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"ສະແດງຕົວເລືອກປີ"</string>
- <string name="date_input_title" msgid="3010396677286327048">"ເລືອກວັນທີ"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"ປ້ອນວັນທີແລ້ວ"</string>
- <string name="date_input_label" msgid="5194825853981987218">"ວັນທີ"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"ປ້ອນວັນທີແລ້ວ: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"ບໍ່ມີ"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"ວັນທີທີ່ບໍ່ອະນຸຍາດ: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"ວັນທີບໍ່ກົງກັບຮູບແບບທີ່ຄາດໄວ້: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"ວັນທີຢູ່ນອກໄລຍະປີທີ່ຄາດໄວ້ %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"ສະຫຼັບໄປໃຊ້ໂໝດປ້ອນຂໍ້ມູນປະຕິທິນ"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"ສະຫຼັບໄປໃຊ້ໂໝດປ້ອນຂໍ້ຄວາມ"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"ເລື່ອນເພື່ອສະແດງປີຫຼັງຈາກນີ້"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"ເລື່ອນເພື່ອສະແດງປີກ່ອນໜ້ານີ້"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"ເລືອກວັນທີ"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"ວັນທີເລີ່ມຕົ້ນ"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"ວັນທີສິ້ນສຸດ"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"ເລື່ອນເພື່ອສະແດງເດືອນຕໍ່ໄປ"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"ເລື່ອນເພື່ອສະແດງເດືອນກ່ອນໜ້າ"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"ຢູ່ໃນໄລຍະ"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"ໃສ່ວັນທີ"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"ອິນພຸດໄລຍະວັນທີບໍ່ຖືກຕ້ອງ"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ບ່ອນຈັບລາກ"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"ຫຍໍ້ຊີດລຸ່ມສຸດລົງ"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ປິດຊີດລຸ່ມສຸດໄວ້"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ຂະຫຍາຍຊີດລຸ່ມສຸດ"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"ຄຳແນະນຳ"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"ສະແດງຄຳແນະນຳ"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"ຫຼັງທ່ຽງ"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"ກ່ອນທ່ຽງ"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"ເລືອກກ່ອນທ່ຽງ ຫຼື ຫຼັງທ່ຽງ"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"ເລືອກຊົ່ວໂມງ"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"ເລືອກນາທີ"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d ໂມງ"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d ຊົ່ວໂມງ"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d ນາທີ"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"ນາທີ"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"ຊົ່ວໂມງ"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"ສຳລັບນາທີ"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ສຳລັບຊົ່ວໂມງ"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-lt/strings.xml b/compose/material3/material3/src/androidMain/res/values-lt/strings.xml
deleted file mode 100644
index 2d449ad..0000000
--- a/compose/material3/material3/src/androidMain/res/values-lt/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialogo langas"</string>
- <string name="expanded" msgid="5974471714631304645">"Išskleista"</string>
- <string name="collapsed" msgid="5389587048670450460">"Sutraukta"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Atsisakyti"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Paieška"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Pasiūlymai pateikti toliau"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Pasirinkite datą"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Pasirinkta data"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Perjungti į metų pasirinkimą"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Perbraukite, kad pasirinktumėte metus, arba palieskite, kad grįžtumėte ir vėl pasirinktumėte dieną"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Pakeisti į kitą mėnesį"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Pakeisti į ankstesnį mėnesį"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Eiti į %1$s m."</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Dabartinis pasirinkimas: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Nėra"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Šiandien"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Rodomas metų parinkiklis"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Pasirinkite datą"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Įvesta data"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Data"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Įvesta data: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Nėra"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Data neleidžiama: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Data neatitinka numatyto šablono: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Data nepatenka į numatytų metų diapazoną: %1$s–%2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Perjungti į kalendoriaus įvesties režimą"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Perjungti į teksto įvesties režimą"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Slinkite, kol bus rodomi vėlesni metai"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Slinkite, kol bus rodomi ankstesni metai"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Pasirinkite datas"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Pradžios data"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Pabaigos data"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Slinkite, kol bus rodomas kitas mėnuo"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Slinkite, kol bus rodomas ankstesnis mėnuo"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Diapazone"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Įvesti datas"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Netinkama dienų sekos įvestis"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Vilkimo rankenėlė"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Sutraukti apatinį lapą"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Atsisakyti apatinio lapo"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Išskleisti apatinį lapą"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Patarimas"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Rodyti patarimą"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"popiet"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"priešpiet"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Pasirinkite „priešpiet“ arba „popiet“"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Pasirinkite valandą"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Pasirinkite minutes"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d val."</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d val."</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%d min."</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minutė"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Valanda"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"minutės"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"valandos"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-lv/strings.xml b/compose/material3/material3/src/androidMain/res/values-lv/strings.xml
deleted file mode 100644
index a0e0c58..0000000
--- a/compose/material3/material3/src/androidMain/res/values-lv/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialoglodziņš"</string>
- <string name="expanded" msgid="5974471714631304645">"Izvērsts"</string>
- <string name="collapsed" msgid="5389587048670450460">"Sakļauts"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Noraidīt"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Meklēšana"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Tālāk ir sniegti ieteikumi"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Atlasīt datumu"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Atlasītais datums"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Pāriet uz gada atlasi"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Velciet, lai atlasītu gadu, vai pieskarieties, lai pārietu atpakaļ pie dienas atlases"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Mainīt uz nākamo mēnesi"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Mainīt uz iepriekšējo mēnesi"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Pāriet uz %1$s. gadu"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Pašreizējā atlase: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Nav"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Šodien"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Redzams gada atlasītājs"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Atlasīt datumu"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Ievadītais datums"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Datums"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Ievadītais datums: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Nav"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Datums nav atļauts: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Datums neatbilst paredzētajam formātam: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Datums nav paredzētajā gadu diapazonā (%1$s.–%2$s. g.)"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Pārslēgties uz kalendāra ievades režīmu"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Pārslēgties uz teksta ievades režīmu"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Lai rādītu nākamos gadus, ritiniet"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Lai rādītu iepriekšējos gadus, ritiniet"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Atlasiet datumus"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Sākuma datums"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Beigu datums"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Lai rādītu nākamo mēnesi, ritiniet"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Lai rādītu iepriekšējo mēnesi, ritiniet"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Atlasītajā diapazonā"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Ievadiet datumus"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Ievadīts nederīgs datumu diapazons."</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Vilkšanas turis"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Sakļaut ekrāna apakšdaļas lapu"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Noraidīt ekrāna apakšdaļas lapu"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Izvērst ekrāna apakšdaļas lapu"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Rīka padoms"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Rādīt rīka padomu"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Atlasīt “AM” (priekšpusdienā) vai “PM” (pēcpusdienā)"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Atlasīt stundu"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Atlasīt minūtes"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"Minūtes: %1$d"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minūtes"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Stundas"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"(minūtes)"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"(stundas)"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-mk/strings.xml b/compose/material3/material3/src/androidMain/res/values-mk/strings.xml
deleted file mode 100644
index 66bfb69..0000000
--- a/compose/material3/material3/src/androidMain/res/values-mk/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Дијалог"</string>
- <string name="expanded" msgid="5974471714631304645">"Проширено"</string>
- <string name="collapsed" msgid="5389587048670450460">"Собрано"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Отфрли"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Пребарување"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Предлозите се наведени подолу"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Изберете датум"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Избран датум"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Префрли на бирање година"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Повлечете за да изберете година или допрете за да се вратите на бирање ден"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Промени на следниот месец"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Промени на претходниот месец"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Одете на годината %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Тековен избор: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Нема"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Денес"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Избирачот на година е видлив"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Изберете датум"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Внесен датум"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Датум"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Внесен датум: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Нема"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Датумот не е дозволен: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Не се совпаѓа со очекуваната шема: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Датумот не е во очекуваниот опсег на години %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Префрли на режим за внесување во календарот"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Префрли на режим за внесување текст"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Лизгајте за да ги прикажете подоцнежните години"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Лизгајте за да ги прикажете претходните години"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Изберете датуми"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Датум на започнување"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Датум на завршување"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Лизгајте за да го прикажете следниот месец"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Лизгајте за да го прикажете претходниот месец"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Во опсег"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Внесете датуми"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Внесовте неважечки временски период"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Рачка за влечење"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Собери го долниот лист"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Отфрли го долниот лист"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Прошири го долниот лист"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Совет за алатка"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Прикажи совет за алатка"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"попладне"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"претпладне"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Изберете претпладне или попладне"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Изберете час"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Изберете минути"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d часот"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d часот"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d минути"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Минута"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Час"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"за минути"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"за час"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ml/strings.xml b/compose/material3/material3/src/androidMain/res/values-ml/strings.xml
deleted file mode 100644
index 1e0fea1..0000000
--- a/compose/material3/material3/src/androidMain/res/values-ml/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"ഡയലോഗ്"</string>
- <string name="expanded" msgid="5974471714631304645">"വിപുലീകരിച്ചത്"</string>
- <string name="collapsed" msgid="5389587048670450460">"ചുരുക്കിയത്"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"ഡിസ്മിസ് ചെയ്യുക"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"തിരയുക"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"നിദ്ദേശങ്ങൾ ചുവടെയുണ്ട്"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"തീയതി തിരഞ്ഞെടുക്കുക"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"തിരഞ്ഞെടുത്ത തീയതി"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"വർഷം തിരഞ്ഞെടുക്കുന്നതിലേക്ക് മാറുക"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"വർഷം തിരഞ്ഞെടുക്കാൻ സ്വൈപ്പ് ചെയ്യുക അല്ലെങ്കിൽ ദിവസം തിരഞ്ഞെടുക്കുന്നതിലേക്ക് തിരികെ പോകാൻ ടാപ്പ് ചെയ്യുക"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"അടുത്ത മാസത്തിലേക്ക് മാറ്റുക"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"മുമ്പത്തെ മാസത്തിലേക്ക് മാറ്റുക"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"%1$s എന്ന വർഷത്തിലേക്ക് പോകുക"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"നിലവിലെ തിരഞ്ഞെടുപ്പ്: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"ഒന്നുമില്ല"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"ഇന്ന്"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"വർഷ പിക്കർ ദൃശ്യമാണ്"</string>
- <string name="date_input_title" msgid="3010396677286327048">"തീയതി തിരഞ്ഞെടുക്കുക"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"നൽകിയ തീയതി"</string>
- <string name="date_input_label" msgid="5194825853981987218">"തീയതി"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"നൽകിയ തീയതി: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"ഒന്നുമില്ല"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"തീയതി അനുവദനീയമല്ല: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"പ്രതീക്ഷിച്ച പാറ്റേണുമായി തീയതി പൊരുത്തപ്പെടുന്നില്ല: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"പ്രതീക്ഷിക്കുന്ന കാലയളവിലെ വർഷമല്ല നൽകിയ തീയതിയുടേത് %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"കലണ്ടർ ഇൻപുട്ട് മോഡിലേക്ക് മാറുക"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"ടെക്സ്റ്റ് ഇൻപുട്ട് മോഡിലേക്ക് മാറുക"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"പിന്നീടുള്ള വർഷങ്ങൾ കാണിക്കാൻ സ്ക്രോൾ ചെയ്യുക"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"മുൻ വർഷങ്ങൾ കാണിക്കാൻ സ്ക്രോൾ ചെയ്യുക"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"തീയതികൾ തിരഞ്ഞെടുക്കുക"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"ആരംഭിക്കുന്ന തീയതി"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"അവസാനിക്കുന്ന തീയതി"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"\'അടുത്ത മാസം\' കാണിക്കാൻ സ്ക്രോൾ ചെയ്യുക"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"മുമ്പത്തെ മാസം കാണിക്കാൻ സ്ക്രോൾ ചെയ്യുക"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"പരിധിയിൽ"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"തീയതികൾ നൽകുക"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"തീയതി ശ്രേണി ഇൻപുട്ട് അസാധുവാണ്"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"വലിച്ചിടുന്നതിനുള്ള ഹാൻഡിൽ"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"ബോട്ടം ഷീറ്റ് ചുരുക്കുക"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ബോട്ടം ഷീറ്റ് ഡിസ്മിസ് ചെയ്യുക"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ബോട്ടം ഷീറ്റ് വികസിപ്പിക്കുക"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"ടൂൾടിപ്പ്"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"ടൂൾടിപ്പ് കാണിക്കുക"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"AM അല്ലെങ്കിൽ PM തിരഞ്ഞെടുക്കുക"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"മണിക്കൂർ തിരഞ്ഞെടുക്കുക"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"മിനിറ്റ് തിരഞ്ഞെടുക്കുക"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d മണി"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d മ."</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d മിനിറ്റ്"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"മിനിറ്റ്"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"മണിക്കൂർ"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"മിനിറ്റ് നേരത്തേക്ക്"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"മണിക്കൂർ നേരത്തേക്ക്"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-mn/strings.xml b/compose/material3/material3/src/androidMain/res/values-mn/strings.xml
deleted file mode 100644
index e5b4acb..0000000
--- a/compose/material3/material3/src/androidMain/res/values-mn/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Харилцах цонх"</string>
- <string name="expanded" msgid="5974471714631304645">"Дэлгэсэн"</string>
- <string name="collapsed" msgid="5389587048670450460">"Хураасан"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Үл хэрэгсэх"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Хайх"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Доорх зөвлөмжүүд"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Огноо сонгох"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Сонгосон огноо"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Жил сонгох руу сэлгэх"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Он сонгохын тулд шудрах эсвэл өдөр сонгох руу буцааж сэлгэхийн тулд товшино уу"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Дараагийн сар луу өөрчлөх"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Өмнөх сар луу өөрчлөх"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"%1$s он руу шилжих"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Одоогийн сонголт: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Байхгүй"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Өнөөдөр"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Он сонгогч харагдаж байна"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Огноо сонгох"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Оруулсан огноо"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Огноо"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Оруулсан огноо: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Байхгүй"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Зөвшөөрөөгүй огноо: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Огноо нь тооцоолсон хээтэй таарахгүй байна: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Тооцоолсон оны %1$s - %2$s мужаас гарсан огноо"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Календарийн орох горим руу сэлгэх"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Текст оруулах горим руу сэлгэх"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Дараагийн жилүүдийг харуулахын тулд гүйлгэнэ үү"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Өмнөх жилүүдийг харуулахын тулд гүйлгэнэ үү"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Огноо сонгох"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Эхлэх огноо"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Дуусах огноо"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Дараагийн сарыг харуулахын тулд гүйлгэнэ үү"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Өмнөх сарыг харуулахын тулд гүйлгэнэ үү"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Хүрээнд байгаа"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Огноо оруулах"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Хугацааны интервалын оролт буруу байна"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Чирэх бариул"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Доод хүснэгтийг хураах"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Доод хүснэгтийг хаах"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Доод хүснэгтийг дэлгэх"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Зөвлөмж"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Зөвлөмж харуулах"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"ҮХ"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"ҮӨ"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"ҮӨ эсвэл ҮХ эсэхийг сонгоно уу"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Цаг сонгох"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Минут сонгоно уу"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d цаг"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d цаг"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d минут"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Минут"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Цаг"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"минутын турш"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"цагийн турш"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-mr/strings.xml b/compose/material3/material3/src/androidMain/res/values-mr/strings.xml
deleted file mode 100644
index ef3edc5..0000000
--- a/compose/material3/material3/src/androidMain/res/values-mr/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"डायलॉग"</string>
- <string name="expanded" msgid="5974471714631304645">"विस्तारित केला"</string>
- <string name="collapsed" msgid="5389587048670450460">"कोलॅप्स केला"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"डिसमिस करा"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"शोधा"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"सूचना खाली आहेत"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"तारीख निवडा"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"निवडलेली तारीख"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"वर्ष निवडणे वर स्विच करा"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"वर्ष निवडण्यासाठी स्वाइप करा, किंवा दिवस निवडण्यावर परत स्विच करण्यासाठी टॅप करा"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"पुढील महिन्यावर बदला"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"मागील महिन्यावर बदला"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"%1$s वर्षावर नेव्हिगेट करा"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"सद्य निवड: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"काहीही नाही"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"आज"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"वर्ष पिकर दृश्यमान आहे"</string>
- <string name="date_input_title" msgid="3010396677286327048">"तारीख निवडा"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"एंटर केलेली तारीख"</string>
- <string name="date_input_label" msgid="5194825853981987218">"तारीख"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"एंटर केली तारीख: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"काहीही नाही"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"अशा तारखेला अनुमती नाही: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"तारीख ही अपेक्षित पॅटर्नशी जुळत नाही: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"तारीख ही %1$s - %2$s या अपेक्षित रेंजच्या बाहेरची आहे"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"कॅलेंडर इनपुट मोडवर स्विच करा"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"टेक्स्ट इनपुट मोडवर स्विच करा"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"नंतरची वर्ष दाखवण्यासाठी स्क्रोल करा"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"पूर्वीची वर्ष दाखवण्यासाठी स्क्रोल करा"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"तारखा निवडा"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"सुरू होण्याची तारीख"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"संपण्याची तारीख"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"पुढील महिना दाखवण्यासाठी स्क्रोल करा"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"मागील महिना दाखवण्यासाठी स्क्रोल करा"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"रेंजमध्ये"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"तारखा एंटर करा"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"तारीख रेंजचे इनपुट चुकीचे आहे"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ड्रॅग हॅंडल"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"तळाशी असलेली शीट कोलॅप्स करा"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"तळाशी असलेली शीट डिसमिस करा"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"तळाशी असलेली शीट विस्तारीत करा"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"टूलटिप"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"टूलटिप दाखवा"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"AM किंवा PM निवडा"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"तास निवडा"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"मिनिटे निवडा"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d वाजता"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d तास"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d मिनिटे"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"मिनिट"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"तास"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"मिनिटांसाठी"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"तासासाठी"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ms/strings.xml b/compose/material3/material3/src/androidMain/res/values-ms/strings.xml
deleted file mode 100644
index bf121e9..0000000
--- a/compose/material3/material3/src/androidMain/res/values-ms/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialog"</string>
- <string name="expanded" msgid="5974471714631304645">"Dikembangkan"</string>
- <string name="collapsed" msgid="5389587048670450460">"Dikuncupkan"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Ketepikan"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Carian"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Cadangan di bawah"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Pilih tarikh"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Tarikh dipilih"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Beralih kepada pemilihan tahun"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Leret untuk memilih tahun atau ketik untuk bertukar kembali kepada pemilihan hari"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Tukar kepada bulan seterusnya"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Tukar kepada bulan sebelumnya"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Navigasi ke tahun %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Pilihan semasa: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Tiada"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Hari ini"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Pemilih tahun kelihatan"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Pilih tarikh"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Tarikh yang dimasukkan"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Tarikh"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Tarikh yang dimasukkan: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Tiada"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Tarikh yang tidak dibenarkan: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Tarikh tidak sepadan dengan corak yang dijangkakan: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Tarikh di luar julat tahun yang dijangkakan %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Beralih kepada mod input kalendar"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Beralih kepada mod input teks"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Tatal untuk menunjukkan tahun kemudian"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Tatal untuk menunjukkan tahun terdahulu"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Pilih tarikh"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Tarikh mula"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Tarikh tamat"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Tatal untuk menunjukkan bulan seterusnya"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Tatal untuk menunjukkan bulan sebelumnya"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Dalam liputan"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Masukkan tarikh"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Input julat tarikh tidak sah"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Pemegang seret"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Kuncupkan helaian bawah"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Ketepikan helaian bawah"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Kembangkan helaian bawah"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Tip alat"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Tunjukkan tip alat"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"P/M"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"PG"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Pilih PG atau PTG/MLM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Pilih jam"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Pilih minit"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"Pukul %1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d jam"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minit"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minit"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Jam"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"selama # minit"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"selama # jam"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-my/strings.xml b/compose/material3/material3/src/androidMain/res/values-my/strings.xml
deleted file mode 100644
index 5131a8f..0000000
--- a/compose/material3/material3/src/androidMain/res/values-my/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"ဒိုင်ယာလော့"</string>
- <string name="expanded" msgid="5974471714631304645">"ချဲ့ထားသည်"</string>
- <string name="collapsed" msgid="5389587048670450460">"ခေါက်ထားသည်"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"ပယ်ရန်"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"ရှာဖွေရန်"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"အကြံပြုချက်များ အောက်တွင်ရှိသည်"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"ရက်စွဲရွေးရန်"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"ရွေးထားသည့် ရက်စွဲ"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"နှစ်ရွေးခြင်းသို့ ပြောင်းရန်"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"ခုနှစ်ရွေးချယ်ရန် ပွတ်ဆွဲပါ (သို့) ရက်ရွေးချယ်ခြင်းသို့ ပြန်ရန် တို့ပါ"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"နောက်လသို့ ပြောင်းရန်"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"ယခင်လသို့ ပြောင်းရန်"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"%1$s ခုနှစ်သို့ သွားရန်"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"လက်ရှိ ရွေးချယ်မှု- %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"မရှိ"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"ယနေ့"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"ခုနှစ်ရွေးချယ်ရေးစနစ်ကို မြင်ရသည်"</string>
- <string name="date_input_title" msgid="3010396677286327048">"ရက်စွဲရွေးရန်"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"ထည့်ထားသော ရက်စွဲ"</string>
- <string name="date_input_label" msgid="5194825853981987218">"ရက်စွဲ"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"ထည့်ထားသော ရက်စွဲ- %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"မရှိ"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"ရက်စွဲကို ခွင့်ပြုမထားပါ- %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"ရက်စွဲသည် မျှော်မှန်းထားသော ပုံစံနှင့် မကိုက်ညီပါ- %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"ရက်စွဲသည် မျှော်မှန်းထားသော နှစ်အပိုင်းအခြား %1$s - %2$s တွင် မပါဝင်ပါ"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"ပြက္ခဒိန် လက်ကွက်ထည့်သွင်းနည်းသို့ ပြောင်းရန်"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"စာရိုက်နည်းသို့ ပြောင်းရန်"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"နောက်ပိုင်းနှစ်များ ပြရန် လှိမ့်ပါ"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"ယခင်နှစ်များ ပြရန် လှိမ့်ပါ"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"ရက်စွဲများရွေးပါ"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"စတင်ရက်"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"ပြီးဆုံးရက်"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"လာမည့်လကို ပြရန် လှိမ့်ပါ"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"ယခင်လကို ပြရန် လှိမ့်ပါ"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"အပိုင်းအခြားအတွင်း"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"ရက်စွဲများထည့်ပါ"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"ဒေတာအပိုင်းအခြား ထည့်သွင်းမှု မမှန်ပါ"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ဖိဆွဲအထိန်း"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"အောက်ခြေအပိုဆောင်း စာမျက်နှာကို ချုံ့သည်"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"အောက်ခြေအပိုဆောင်း စာမျက်နှာကို ပယ်သည်"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"အောက်ခြေအပိုဆောင်း စာမျက်နှာကို ချဲ့သည်"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"အကြံပြုချက်ပြ ပေါ့အပ် ဝင်းဒိုး"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"အကြံပြုချက်ပြ ပေါ့အပ်ဝင်းဒိုး ပြရန်"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"AM (သို့) PM ရွေးရန်"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"နာရီ ရွေးရန်"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"မိနစ် ရွေးရန်"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d နာရီ"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d နာရီ"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d မိနစ်"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"မိနစ်"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"နာရီ"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"မိနစ်ကြာ"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"နာရီကြာ"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-nb/strings.xml b/compose/material3/material3/src/androidMain/res/values-nb/strings.xml
deleted file mode 100644
index 4274c3f..0000000
--- a/compose/material3/material3/src/androidMain/res/values-nb/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialogboks"</string>
- <string name="expanded" msgid="5974471714631304645">"Vises"</string>
- <string name="collapsed" msgid="5389587048670450460">"Skjult"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Lukk"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Søk"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Du finner forslag nedenfor"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Velg dato"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Valgt dato"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Bytt til å velge et år"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Sveip for å velge år, eller trykk for å bytte tilbake til valg av dag"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Endre til neste måned"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Endre til forrige måned"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Gå til år %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Valgt: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Ingen"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"I dag"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Årsvelgeren er synlig"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Velg dato"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Angitt dato"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Dato"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Angitt dato: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Ingen"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Datoen er ikke tillatt: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Datoen matcher ikke det forventede mønsteret: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Datoen er utenfor det forventede årsintervallet %1$s–%2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Bytt til kalendermodus for inndata"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Bytt til tekstmodus for inndata"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Rull for å vise senere år"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Rull for å vise tidligere år"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Velg datoer"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Startdato"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Sluttdato"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Rull for å vise den neste måneden"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Rull for å vise den forrige måneden"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Innen rekkevidde"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Legg inn datoer"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"En ugyldig dataperiode er skrevet inn"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Håndtak"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Skjul feltet nederst"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Lukk feltet nederst"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Vis feltet nederst"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Verktøytips"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Vis verktøytips"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Velg AM eller PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Velg time"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Velg minutter"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d timer"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutter"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minutt"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Time"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"for minutter"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"for timer"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ne/strings.xml b/compose/material3/material3/src/androidMain/res/values-ne/strings.xml
deleted file mode 100644
index f6180e9..0000000
--- a/compose/material3/material3/src/androidMain/res/values-ne/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"डायलग"</string>
- <string name="expanded" msgid="5974471714631304645">"एक्स्पान्ड गरियो"</string>
- <string name="collapsed" msgid="5389587048670450460">"कोल्याप्स गरियो"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"हटाउनुहोस्"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"खोज्नुहोस्"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"सुझावहरू तल दिइएका छन्"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"मिति चयन गर्नुहोस्"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"चयन गरिएको मिति"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"साल चयन गर्ने फिल्डमा जानुहोस्"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"कुनै साल छनौट गर्न स्वाइप गर्नुहोस् वा दिन चयन गर्न ट्याप गर्नुहोस्"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"हाल चयन गरिएको महिना परिवर्तन गरी आगामी महिना बनाउनुहोस्"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"हाल चयन गरिएको महिना परिवर्तन गरी अघिल्लो महिना बनाउनुहोस्"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"साल %1$s मा जानुहोस्"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"हालको छनौट: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"कुनै पनि होइन"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"आज"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"साल पिकर देखिएको छ"</string>
- <string name="date_input_title" msgid="3010396677286327048">"मिति चयन गर्नुहोस्"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"हालिएको मिति"</string>
- <string name="date_input_label" msgid="5194825853981987218">"मिति"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"हालिएको मिति: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"कुनै पनि होइन"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"यो मिति हाल्न पाइँदैन: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"हालिएको मितिको ढाँचा अपेक्षित ढाँचासँग मिलेन: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"हालिएको मिति सालको अपेक्षित दायरा (%1$s - %2$s) भित्र पर्दैन"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"पात्रोको इनपुट मोड प्रयोग गर्नुहोस्"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"टेक्स्ट इनपुट मोड प्रयोग गर्नुहोस्"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"आगामी सालहरूको जानकारी हेर्न स्क्रोल गर्नुहोस्"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"विगतका सालहरूको जानकारी हेर्न स्क्रोल गर्नुहोस्"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"मितिहरू चयन गर्नुहोस्"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"सुरु हुने मिति"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"समापन हुने मिति"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"अर्को महिनाको जानकारी हेर्न स्क्रोल गर्नुहोस्"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"गत महिनाको जानकारी हेर्न स्क्रोल गर्नुहोस्"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"चयन गरिएका मितिभित्र पर्ने"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"मितिहरू हाल्नुहोस्"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"मितिको अवैध दायरा तोकियो"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ड्र्याग ह्यान्डल"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"पुछारको पाना कोल्याप्स गर्नुहोस्"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"पुछारको पाना हटाउनुहोस्"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"पुछारको पाना एक्स्पान्ड गर्नुहोस्"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"टुलटिप"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"टुलटिप देखाइयोस्"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"पूर्वाह्न वा अपराह्न चयन गर्नुहोस्"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"घण्टा चयन गर्नुहोस्"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"मिनेट चयन गर्नुहोस्"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d बजे"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d घण्टा"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d मिनेट"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"मिनेट"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"घण्टा"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"मिनेटका लागि"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"घण्टाका लागि"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-nl/strings.xml b/compose/material3/material3/src/androidMain/res/values-nl/strings.xml
deleted file mode 100644
index ca8d0ec..0000000
--- a/compose/material3/material3/src/androidMain/res/values-nl/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialoogvenster"</string>
- <string name="expanded" msgid="5974471714631304645">"Uitgevouwen"</string>
- <string name="collapsed" msgid="5389587048670450460">"Samengevouwen"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Sluiten"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Zoeken"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Suggesties hieronder"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Datum selecteren"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Geselecteerde datum"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Schakelaar om een jaar te selecteren"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Swipe om een jaar te selecteren of tik om terug te gaan en een dag te selecteren"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Naar volgende maand gaan"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Naar vorige maand gaan"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Ga naar jaar %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Huidige selectie: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Geen"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Vandaag"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Jaarselectie zichtbaar"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Datum selecteren"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Opgegeven datum"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Datum"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Opgegeven datum: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Geen"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Datum niet toegestaan: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"De datum komt niet overeen met het verwachte patroon: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Datum buiten het verwachte jaarbereik %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Overschakelen naar agenda-invoermodus"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Overschakelen naar tekstinvoermodus"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Scroll om latere jaren te tonen"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Scroll om eerdere jaren te tonen"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Datums selecteren"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Startdatum"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Einddatum"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Scroll om de volgende maand te tonen"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Scroll om de vorige maand te tonen"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Binnen bereik"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Datums opgeven"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Ongeldige invoer voor periode"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Handgreep voor slepen"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Blad onderaan samenvouwen"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Blad onderaan sluiten"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Blad onderaan uitvouwen"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Tooltip"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Tooltip tonen"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"AM of PM selecteren"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Uur selecteren"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Minuten selecteren"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d uur"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d uur"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minuten"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minuut"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Uur"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"voor minuten"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"voor uur"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-or/strings.xml b/compose/material3/material3/src/androidMain/res/values-or/strings.xml
deleted file mode 100644
index b8af623..0000000
--- a/compose/material3/material3/src/androidMain/res/values-or/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"ଡାଏଲଗ"</string>
- <string name="expanded" msgid="5974471714631304645">"ବିସ୍ତାର କରାଯାଇଛି"</string>
- <string name="collapsed" msgid="5389587048670450460">"ସଙ୍କୁଚିତ କରାଯାଇଛି"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"ଖାରଜ କରନ୍ତୁ"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"ପରାମର୍ଶ ତଳେ ଦିଆଯାଇଛି"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"ତାରିଖ ଚୟନ କରନ୍ତୁ"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"ଚୟନିତ ତାରିଖ"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"ବର୍ଷ ଚୟନ କରିବାକୁ ସ୍ୱିଚ କରନ୍ତୁ"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"ଏକ ବର୍ଷ ଚୟନ କରିବା ପାଇଁ ସ୍ୱାଇପ କରନ୍ତୁ କିମ୍ବା ଏକ ଦିନ ଚୟନ କରିବା ପାଇଁ ପୁଣି ସ୍ୱିଚ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"ପରବର୍ତ୍ତୀ ମାସକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"ପୂର୍ବବର୍ତ୍ତୀ ମାସକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"%1$s ବର୍ଷକୁ ନାଭିଗେଟ କରନ୍ତୁ"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"ବର୍ତ୍ତମାନର ଚୟନ: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"କିଛି ନାହିଁ"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"ଆଜି"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"ବର୍ଷ ପିକର ଦେଖାଯାଉଛି"</string>
- <string name="date_input_title" msgid="3010396677286327048">"ତାରିଖ ଚୟନ କରନ୍ତୁ"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"ଲେଖାଯାଇଥିବା ତାରିଖ"</string>
- <string name="date_input_label" msgid="5194825853981987218">"ତାରିଖ"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"ଲେଖାଯାଇଥିବା ତାରିଖ: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"କିଛି ନାହିଁ"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"ତାରିଖକୁ ଅନୁମତି ଦିଆଯାଇନାହିଁ: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"ଆଶା କରାଯାଉଥିବା ପାଟର୍ନ ସହ ତାରିଖ ମେଳ ହେଉନାହିଁ: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"ତାରିଖଟି ଆଶା କରାଯାଉଥିବା ବର୍ଷ ରେଞ୍ଜ %1$s - %2$sରୁ ବାହାରେ ଅଛି"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"କେଲେଣ୍ଡର ଇନପୁଟ ମୋଡକୁ ସ୍ୱିଚ କରନ୍ତୁ"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"ଟେକ୍ସଟ ଇନପୁଟ ମୋଡକୁ ସ୍ୱିଚ କରନ୍ତୁ"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"ପର ବର୍ଷଗୁଡ଼ିକ ଦେଖାଇବା ପାଇଁ ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"ପୂର୍ବ ବର୍ଷଗୁଡ଼ିକ ଦେଖାଇବା ପାଇଁ ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"ତାରିଖଗୁଡ଼ିକ ଚୟନ କରନ୍ତୁ"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"ଆରମ୍ଭ ତାରିଖ"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"ଶେଷ ତାରିଖ"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"ପରବର୍ତ୍ତୀ ମାସ ଦେଖାଇବା ପାଇଁ ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"ପୂର୍ବବର୍ତ୍ତୀ ମାସ ଦେଖାଇବା ପାଇଁ ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"ରେଞ୍ଜରେ ଅଛି"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"ତାରିଖଗୁଡ଼ିକ ଲେଖନ୍ତୁ"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"ଅବୈଧ ତାରିଖ ରେଞ୍ଜ ଇନପୁଟ"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ଡ୍ରାଗ ହେଣ୍ଡେଲ"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"ବଟମ ସିଟକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ବଟମ ସିଟକୁ ଖାରଜ କରନ୍ତୁ"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ବଟମ ସିଟକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"ଟୁଲଟିପ"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"ଟୁଲଟିପ ଦେଖାନ୍ତୁ"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"AM କିମ୍ବା PM ଚୟନ କରନ୍ତୁ"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"ଘଣ୍ଟା ଚୟନ କରନ୍ତୁ"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"ମିନିଟ ଚୟନ କରନ୍ତୁ"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$dଟା"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d ଘଣ୍ଟା"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d ମିନିଟ"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"ମିନିଟ"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"ଘଣ୍ଟା"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"ମିନିଟ ପାଇଁ"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ଘଣ୍ଟା ପାଇଁ"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-pa/strings.xml b/compose/material3/material3/src/androidMain/res/values-pa/strings.xml
deleted file mode 100644
index 30a8002..0000000
--- a/compose/material3/material3/src/androidMain/res/values-pa/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"ਵਿੰਡੋ"</string>
- <string name="expanded" msgid="5974471714631304645">"ਵਿਸਤਾਰ ਕੀਤਾ ਗਿਆ"</string>
- <string name="collapsed" msgid="5389587048670450460">"ਸਮੇਟਿਆ ਗਿਆ"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"ਖਾਰਜ ਕਰੋ"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"ਖੋਜੋ"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"ਸੁਝਾਅ ਹੇਠਾਂ ਹਨ"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"ਤਾਰੀਖ ਚੁਣੋ"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"ਚੁਣੀ ਗਈ ਤਾਰੀਖ"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"ਸਾਲ ਚੁਣਨ ਲਈ ਸਵਿੱਚ ਕਰੋ"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"ਕੋਈ ਸਾਲ ਚੁਣਨ ਲਈ ਸਵਾਈਪ ਕਰੋ ਜਾਂ ਕੋਈ ਦਿਨ ਚੁਣਨ ਲਈ ਵਾਪਸ ਜਾਣ ਵਾਸਤੇ ਟੈਪ ਕਰੋ"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"ਅਗਲੇ ਮਹੀਨੇ \'ਤੇ ਜਾਓ"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"ਪਿਛਲੇ ਮਹੀਨੇ \'ਤੇ ਜਾਓ"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"ਸਾਲ %1$s \'ਤੇ ਜਾਓ"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"ਮੌਜੂਦਾ ਚੋਣ: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"ਕੋਈ ਨਹੀਂ"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"ਅੱਜ"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"ਸਾਲ ਚੋਣਕਾਰ ਦਿਖਣਯੋਗ ਹੈ"</string>
- <string name="date_input_title" msgid="3010396677286327048">"ਤਾਰੀਖ ਚੁਣੋ"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"ਦਾਖਲ ਕੀਤੀ ਗਈ ਤਾਰੀਖ"</string>
- <string name="date_input_label" msgid="5194825853981987218">"ਤਾਰੀਖ"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"ਦਾਖਲ ਕੀਤੀ ਗਈ ਤਾਰੀਖ: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"ਕੋਈ ਨਹੀਂ"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"ਇਸ ਤਾਰੀਖ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"ਤਾਰੀਖ ਸੰਭਾਵਿਤ ਪੈਟਰਨ ਨਾਲ ਮੇਲ ਨਹੀਂ ਖਾਂਦੀ: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"ਤਾਰੀਖ ਸੰਭਾਵਿਤ ਸਾਲ ਦੀ ਰੇਂਜ ਤੋਂ ਬਾਹਰ ਹੈ %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"ਕੈਲੰਡਰ ਇਨਪੁੱਟ ਮੋਡ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"ਲਿਖਤ ਇਨਪੁੱਟ ਮੋਡ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"ਬਾਅਦ ਵਾਲੇ ਸਾਲਾਂ ਨੂੰ ਦਿਖਾਉਣ ਲਈ ਸਕ੍ਰੋਲ ਕਰੋ"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"ਪਹਿਲਾਂ ਵਾਲੇ ਸਾਲਾਂ ਨੂੰ ਦਿਖਾਉਣ ਲਈ ਸਕ੍ਰੋਲ ਕਰੋ"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"ਤਾਰੀਖਾਂ ਚੁਣੋ"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"ਸ਼ੁੁਰੂਆਤੀ ਤਾਰੀਖ"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"ਸਮਾਪਤੀ ਤਾਰੀਖ"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"ਅਗਲਾ ਮਹੀਨਾ ਦਿਖਾਉਣ ਲਈ ਸਕ੍ਰੋਲ ਕਰੋ"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"ਪਿਛਲਾ ਮਹੀਨਾ ਦਿਖਾਉਣ ਲਈ ਸਕ੍ਰੋਲ ਕਰੋ"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"ਰੇਂਜ ਵਿੱਚ"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"ਤਾਰੀਖਾਂ ਦਾਖਲ ਕਰੋ"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"ਇਨਪੁੱਟ ਕੀਤੀ ਗਈ ਤਾਰੀਖ ਦੀ ਰੇਂਜ ਅਵੈਧ ਹੈ"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ਘਸੀਟਣ ਵਾਲਾ ਹੈਂਡਲ"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"ਹੇਠਲੀ ਸ਼ੀਟ ਨੂੰ ਸਮੇਟੋ"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ਹੇਠਲੀ ਸ਼ੀਟ ਨੂੰ ਖਾਰਜ ਕਰੋ"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ਹੇਠਲੀ ਸ਼ੀਟ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"ਟੂਲ-ਟਿੱਪ"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"ਟੂਲ-ਟਿੱਪ ਦਿਖਾਓ"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"AM ਜਾਂ PM ਚੁਣੋ"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"ਘੰਟਾ ਚੁਣੋ"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"ਮਿੰਟ ਚੁਣੋ"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d ਵਜੇ"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d ਘੰਟੇ"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d ਮਿੰਟ"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"ਮਿੰਟ"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"ਘੰਟੇ"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"ਮਿੰਟਾਂ ਲਈ"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ਘੰਟੇ ਲਈ"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-pl/strings.xml b/compose/material3/material3/src/androidMain/res/values-pl/strings.xml
deleted file mode 100644
index e2e9c8c..0000000
--- a/compose/material3/material3/src/androidMain/res/values-pl/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialog"</string>
- <string name="expanded" msgid="5974471714631304645">"Rozwinięte"</string>
- <string name="collapsed" msgid="5389587048670450460">"Zwinięte"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Zamknij"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Wyszukiwanie"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Propozycje znajdziesz poniżej"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Wybierz datę"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Wybrana data"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Przełącz na wybór roku"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Przesuń, aby wybrać rok, lub wróć do poprzedniej sekcji i wybierz dzień"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Zmień na następny miesiąc"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Zmień na poprzedni miesiąc"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Przejdź do roku %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Obecnie wybrane: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Brak"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Dzisiaj"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Widoczny selektor roku"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Wybierz datę"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Wprowadzono datę"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Data"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Wprowadzono datę: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Brak"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Data niedozwolona: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Data nie pasuje do oczekiwanego wzorca: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Data poza oczekiwanym zakresem lat %1$s–%2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Włącz tryb wprowadzania danych kalendarzowych"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Włącz tryb wprowadzania tekstu"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Przewiń, aby wyświetlić późniejsze lata"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Przewiń, aby wyświetlić wcześniejsze lata"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Wybierz daty"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Data początkowa"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Data końcowa"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Przewiń, aby wyświetlić kolejny miesiąc"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Przewiń, aby wyświetlić poprzedni miesiąc"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"W zasięgu"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Wprowadź daty"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Nieprawidłowy zakres dat"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Uchwyt do przeciągania"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Zwiń planszę dolną"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Zamknij planszę dolną"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Rozwiń planszę dolną"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Etykietka"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Pokaż etykietkę"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Wybierz AM lub PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Wybierz godzinę"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Wybierz minuty"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d godziny"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minut"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minuta"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Godzina"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"na minuty"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"na godzinę"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-pt-rBR/strings.xml b/compose/material3/material3/src/androidMain/res/values-pt-rBR/strings.xml
deleted file mode 100644
index d54b942..0000000
--- a/compose/material3/material3/src/androidMain/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Caixa de diálogo"</string>
- <string name="expanded" msgid="5974471714631304645">"Aberto"</string>
- <string name="collapsed" msgid="5389587048670450460">"Fechado"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Dispensar"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Pesquisa"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Sugestões abaixo"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Selecionar data"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Data selecionada"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Trocar para a seleção de ano"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Deslize para selecionar um ano ou toque para voltar à seleção de dia"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Mudar para o próximo mês"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Mudar para o mês anterior"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Navegar para o ano de %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Seleção atual: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Nenhuma"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Hoje"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Seletor de dia visível"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Selecionar data"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Data inserida"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Data"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Data inserida: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Nenhuma"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Data não permitida: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"A data não está no padrão esperado: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"A data está fora do intervalo de anos esperado %1$s a %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Alternar para o modo de entrada da agenda"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Alternar para o modo de entrada de texto"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Role a tela para mostrar anos posteriores"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Role a tela para mostrar anos anteriores"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Selecionar datas"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Data de início"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Data de término"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Role a tela para mostrar o mês posterior"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Role a tela para mostrar o mês anterior"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Dentro do alcance"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Informar datas"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Período inválido"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Alça de arrastar"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Fechar página inferior"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Dispensar página inferior"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Abrir página inferior"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Dica"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Mostrar dica"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Selecione AM ou PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Selecione a hora"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Selecione os minutos"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$dh"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d horas"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutos"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minuto"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Hora"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"por minutos"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"por hora"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-pt-rPT/strings.xml b/compose/material3/material3/src/androidMain/res/values-pt-rPT/strings.xml
deleted file mode 100644
index ab06fd9..0000000
--- a/compose/material3/material3/src/androidMain/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Caixa de diálogo"</string>
- <string name="expanded" msgid="5974471714631304645">"Expandido"</string>
- <string name="collapsed" msgid="5389587048670450460">"Reduzido"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Ignorar"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Pesquisar"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Sugestões abaixo"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Selecionar data"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Data selecionada"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Mude para a seleção do ano"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Deslize rapidamente para selecionar um ano ou toque para mudar novamente para a seleção do dia"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Mudar para o mês seguinte"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Mudar para o mês anterior"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Navegar para o ano %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Seleção atual: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Nenhuma"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Hoje"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Selecionador de ano visível"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Selecionar data"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Data introduzida"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Data"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Data introduzida: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Nenhuma"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Data não permitida: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"A data não corresponde ao padrão esperado: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Data fora do intervalo de anos esperado: %1$s – %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Mudar para o método de introdução de calendário"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Mudar para o método de introdução de texto"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Desloque a página para mostrar anos posteriores"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Desloque a página para mostrar anos anteriores"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Selecione as datas"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Data de início"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Data de fim"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Desloque a página para mostrar o mês seguinte"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Desloque a página para mostrar o mês anterior"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Dentro do alcance"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Introduza as datas"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Entrada do intervalo de datas inválida"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Indicador para arrastar"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Reduza a secção inferior"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Ignore a secção inferior"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Expanda a secção inferior"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Sugestão"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Mostrar sugestão"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Selecione AM ou PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Selecione a hora"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Selecione os minutos"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d h"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d horas"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutos"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minuto"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Hora"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"para minutos"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"para hora"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-pt/strings.xml b/compose/material3/material3/src/androidMain/res/values-pt/strings.xml
deleted file mode 100644
index d54b942..0000000
--- a/compose/material3/material3/src/androidMain/res/values-pt/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Caixa de diálogo"</string>
- <string name="expanded" msgid="5974471714631304645">"Aberto"</string>
- <string name="collapsed" msgid="5389587048670450460">"Fechado"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Dispensar"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Pesquisa"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Sugestões abaixo"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Selecionar data"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Data selecionada"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Trocar para a seleção de ano"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Deslize para selecionar um ano ou toque para voltar à seleção de dia"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Mudar para o próximo mês"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Mudar para o mês anterior"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Navegar para o ano de %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Seleção atual: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Nenhuma"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Hoje"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Seletor de dia visível"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Selecionar data"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Data inserida"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Data"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Data inserida: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Nenhuma"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Data não permitida: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"A data não está no padrão esperado: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"A data está fora do intervalo de anos esperado %1$s a %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Alternar para o modo de entrada da agenda"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Alternar para o modo de entrada de texto"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Role a tela para mostrar anos posteriores"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Role a tela para mostrar anos anteriores"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Selecionar datas"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Data de início"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Data de término"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Role a tela para mostrar o mês posterior"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Role a tela para mostrar o mês anterior"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Dentro do alcance"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Informar datas"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Período inválido"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Alça de arrastar"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Fechar página inferior"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Dispensar página inferior"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Abrir página inferior"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Dica"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Mostrar dica"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Selecione AM ou PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Selecione a hora"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Selecione os minutos"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$dh"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d horas"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutos"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minuto"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Hora"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"por minutos"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"por hora"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ro/strings.xml b/compose/material3/material3/src/androidMain/res/values-ro/strings.xml
deleted file mode 100644
index 1280988..0000000
--- a/compose/material3/material3/src/androidMain/res/values-ro/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialog"</string>
- <string name="expanded" msgid="5974471714631304645">"Extins"</string>
- <string name="collapsed" msgid="5389587048670450460">"Restrâns"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Respinge"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Caută"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Sugestii mai jos"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Selectează data"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Data selectată"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Comută la selectarea anului"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Glisează pentru a selecta un an sau atinge pentru a reveni la selectarea zilei"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Treci la luna următoare"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Treci la luna anterioară"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Navighează la anul %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Opțiunea selectată: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Fără"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Azi"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Selectorul de an este vizibil"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Selectează data"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Data introdusă"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Dată"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Data introdusă: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Fără"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Data nu este permisă: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Data nu corespunde modelului așteptat: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Data este în afara intervalului de ani %1$s – %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Comută la modul de introducere în calendar"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Comută la modul de introducere a textului"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Derulează pentru a afișa ani ulteriori"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Derulează pentru a afișa ani anteriori"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Selectează datele"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Data de începere"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Data de încheiere"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Derulează pentru a afișa luna următoare"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Derulează pentru a afișa luna anterioară"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"În interval"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Introdu datele"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Intervalul de date introdus nu este valid"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Ghidaj de tragere"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Restrânge foaia din partea de jos"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Închide foaia din partea de jos"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Extinde foaia din partea de jos"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Balon explicativ"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Afișează balonul explicativ"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"p.m."</string>
- <string name="time_picker_am" msgid="5096144640014509074">"a.m."</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Selectează a.m. sau p.m."</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Selectează ora"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Selectează minutele"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"Ora %1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d ore"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minute"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minut"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Oră"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"timp de câteva minute"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"timp de o oră"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ru/strings.xml b/compose/material3/material3/src/androidMain/res/values-ru/strings.xml
deleted file mode 100644
index 8e3b21c..0000000
--- a/compose/material3/material3/src/androidMain/res/values-ru/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Диалоговое окно"</string>
- <string name="expanded" msgid="5974471714631304645">"Развернуто"</string>
- <string name="collapsed" msgid="5389587048670450460">"Свернуто"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Закрыть"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Строка поиска"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Подсказки показаны ниже"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Выбор даты"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Выбранная дата"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Перейти к выбору года"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Проведите по экрану, чтобы выбрать год, или нажмите, чтобы вернуться к выбору дня."</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Перейти к следующему месяцу"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Перейти к предыдущему месяцу"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Переход к %1$s году"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Текущий выбор: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Не выбрано"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Сегодня"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Отображаемый выбор года"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Выберите дату"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Введенная дата"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Дата"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Введенная дата: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Нет"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Недопустимая дата: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Дата не соответствует допустимому шаблону: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Дата не входит в допустимый диапазон: %1$s–%2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Перейти в режим выбора даты"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Перейти в режим ввода текста"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Прокрутите до более поздних лет"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Прокрутите до более ранних лет"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Выберите даты"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Дата начала"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Дата окончания"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Прокрутите до следующего месяца"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Прокрутите до предыдущего месяца"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"День в диапазоне дат"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Введите даты"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Указан недопустимый диапазон дат."</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Маркер перемещения"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Свернуть нижний экран"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Закрыть нижний экран"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Развернуть нижний экран"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Подсказка"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Показать подсказку"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Выбрать AM (до полудня) или PM (после полудня)"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Выбрать час"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Выбрать минуты"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d ч."</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d ч."</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d мин."</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Минуты"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Часы"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"минуты"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"часы"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-si/strings.xml b/compose/material3/material3/src/androidMain/res/values-si/strings.xml
deleted file mode 100644
index e77dc29..0000000
--- a/compose/material3/material3/src/androidMain/res/values-si/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"සංවාදය"</string>
- <string name="expanded" msgid="5974471714631304645">"දිග හරින ලදි"</string>
- <string name="collapsed" msgid="5389587048670450460">"හකුළන ලදි"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"අස් කරන්න"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"සෙවීම"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"පහත යෝජනා"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"දිනය තෝරන්න"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"තේරූ දිනය"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"වසරක් තේරීමට මාරු වන්න"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"වසරක් තේරීමට ස්වයිප් කරන්න, නැතහොත් දිනක් තේරීමට ආපසු මාරු වීමට තට්ටු කරන්න"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"ලබන මාසයට වෙනස් කරන්න"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"කලින් මාසයට වෙනස් කරන්න"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"%1$s වසර වෙත සංචලන කරන්න"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"වත්මන් තේරීම: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"කිසිවක් නැත"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"අද"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"වසර තෝරකය දෘශ්යමානයි"</string>
- <string name="date_input_title" msgid="3010396677286327048">"දිනය තෝරන්න"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"ඇතුළු කළ දිනය"</string>
- <string name="date_input_label" msgid="5194825853981987218">"දිනය"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"එක් කරන්න: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"කිසිවක් නැත"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"දිනය ඉඩ නොදෙයි: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"දිනය අපේක්ෂිත රටාවට නොගැළපෙයි: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"දිනය අපේක්ෂිත වසර පරාසයෙන් පිටත වේ %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"දින දර්ශන ආදාන ප්රකාරයට මාරු වන්න"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"පෙළ ආදාන ප්රකාරයට මාරු වන්න"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"පසු වසර පෙන්වීමට අනුචලන කරන්න"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"පෙර වසර පෙන්වීමට අනුචලන කරන්න"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"දින තෝරන්න"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"ආරම්භක දිනය"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"අවසාන දිනය"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"ඊළඟ මාසය පෙන්වීමට අනුචලන කරන්න"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"පෙර මාසය පෙන්වීමට අනුචලන කරන්න"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"පරාසය තුළ"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"දින ඇතුළු කරන්න"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"අවලංගු දින පරාස ආදානය"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ඇදීම් හැඬලය"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"පහළම පත්රය හකුළන්න"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"පහළම පත්රය අස් කරන්න"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"පහළම පත්රය දිග හරින්න"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"මෙවලම් ඉඟිය"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"මෙවලම් ඉඟිය පෙන්වන්න"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"ප.ව."</string>
- <string name="time_picker_am" msgid="5096144640014509074">"පෙ.ව."</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"පෙ.ව. හෝ ප.ව. තෝරන්න"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"පැය තෝරන්න"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"මිනිත්තු තෝරන්න"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$dට"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"පැය %1$d"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"මිනිත්තු %1$d"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"විනාඩි"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"පැය"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"මිනිත්තු ගණනක් සඳහා"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"පැයක් සඳහා"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-sk/strings.xml b/compose/material3/material3/src/androidMain/res/values-sk/strings.xml
deleted file mode 100644
index baee341..0000000
--- a/compose/material3/material3/src/androidMain/res/values-sk/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialógové okno"</string>
- <string name="expanded" msgid="5974471714631304645">"Rozbalené"</string>
- <string name="collapsed" msgid="5389587048670450460">"Zbalené"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Zavrieť"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Vyhľadávanie"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Návrhy sú nižšie"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Vybrať dátum"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Vybraný dátum"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Prepnúť na výber roka"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Potiahnutím vyberte rok alebo klepnutím prepnite späť na výber dňa"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Zmeniť na nasledujúci mesiac"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Zmeniť na predchádzajúci mesiac"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Prechod na rok %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Aktuálny výber: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Žiadne"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Dnes"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Výber roka je viditeľný"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Vybrať dátum"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Zadaný dátum"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Dátum"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Zadaný dátum: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Žiadny"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Nepovolený dátum: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Dátum nezodpovedá očakávanému vzoru: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Dátum sa nenachádza v očakávanom rozsahu rokov: %1$s – %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Prepnúť na kalendárový režim vstupu"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Prepnúť na textový režim vstupu"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Posunutím si zobrazte neskoršie roky"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Posunutím si zobrazte skoršie roky"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Vyberte dátumy"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Dátum začatia"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Dátum ukončenia"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Posunutím si zobrazte nasledujúci mesiac"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Posunutím si zobrazte predchádzajúci mesiac"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"V rozsahu"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Zadajte dátumy"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Neplatný vstup obdobia"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Presúvadlo"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Zbaliť dolný hárok"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Zavrieť dolný hárok"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Rozbaliť dolný hárok"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Popis"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Zobraziť opis"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Vyberte AM alebo PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Vybrať hodinu"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Vybrať minúty"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d h"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d h"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d min"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minúty"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Hodina"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"minúty"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"hodina"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-sl/strings.xml b/compose/material3/material3/src/androidMain/res/values-sl/strings.xml
deleted file mode 100644
index b01d1ca..0000000
--- a/compose/material3/material3/src/androidMain/res/values-sl/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Pogovorno okno"</string>
- <string name="expanded" msgid="5974471714631304645">"Razširjeno"</string>
- <string name="collapsed" msgid="5389587048670450460">"Strnjeno"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Opusti"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Iskanje"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Predlogi so spodaj"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Izbira datuma"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Izbrani datum"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Preklopi na izbiro leta"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Povlecite, da izberete leto, ali se dotaknite, da preklopite nazaj na izbiranje dneva."</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Spremeni na naslednji mesec"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Spremeni na prejšnji mesec"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Pomik na leto %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Trenutna izbira: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Brez"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Danes"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Izbirnik leta je viden"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Izbira datuma"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Vneseni datum"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Datum"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Vneseni datum: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Brez"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Oblika datuma ni dovoljena: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Datum se ne ujema s pričakovanim vzorcem: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Datum je zunaj pričakovanega razpona let %1$s–%2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Preklop na način vnosa v koledar"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Preklop na način vnosa besedila"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Pomaknite se za prikaz poznejših let."</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Pomaknite se za prikaz zgodnejših let."</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Izberite datume"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Začetni datum"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Končni datum"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Pomaknite se za prikaz naslednjega meseca."</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Pomaknite se za prikaz prejšnjega meseca."</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Znotraj obdobja"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Vnesite datume"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Neveljaven vnos obdobja."</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Ročica za vlečenje"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Strnitev razdelka na dnu zaslona"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Opustitev razdelka na dnu zaslona"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Razširitev razdelka na dnu zaslona"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Opis orodja"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Pokaži opis orodja"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"pop."</string>
- <string name="time_picker_am" msgid="5096144640014509074">"dop."</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Izberite dopoldanski ali popoldanski čas."</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Izbira ure"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Izbira minut"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d min"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Ura"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"za minute"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"za uro"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-sq/strings.xml b/compose/material3/material3/src/androidMain/res/values-sq/strings.xml
deleted file mode 100644
index f0161e9..0000000
--- a/compose/material3/material3/src/androidMain/res/values-sq/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialogu"</string>
- <string name="expanded" msgid="5974471714631304645">"U zgjerua"</string>
- <string name="collapsed" msgid="5389587048670450460">"U palos"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Hiq"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Kërko"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Sugjerimet më poshtë"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Zgjidh datën"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Data e zgjedhur"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Kalo te zgjedhja e një viti"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Rrëshqit shpejt për të zgjedhur një vit ose trokit për të kaluar sërish te zgjedhja e ditës"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Ndrysho te muaji i ardhshëm"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Ndrysho te muaji i kaluar"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Navigo në vitin %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Zgjedhja aktuale: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Asnjë"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Sot"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Zgjedhësi i vitit i dukshëm"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Zgjidh datën"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Data e futur"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Data"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Data e futur: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Asnjë"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Data nuk lejohet: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Data nuk përputhet me motivin e pritur: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Data jashtë diapazonit të pritur të vitit %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Kalo te modaliteti i \"Hyrjes së kalendarit\""</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Kalo te modaliteti i \"Hyrjes së tekstit\""</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Lëviz për të shfaqur vitet e ardhshme"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Lëviz për të shfaqur vitet e mëparshme"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Zgjidh datat"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Data e fillimit"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Data e mbarimit"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Lëviz për të shfaqur muajin e ardhshëm"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Lëviz për të shfaqur muajin e mëparshëm"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Brenda rrezes"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Fut datat"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Hyrje e pavlefshme e diapazonit të datave"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Doreza e zvarritjes"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Palos fletën e poshtme"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Hiq fletën e poshtme"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Zgjero fletën e poshtme"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Këshilla për veglën"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Shfaq këshillat për veglën"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"MD"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"PD"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Zgjidh paradite ose pasdite"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Zgjidh orën"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Përzgjidh minutat"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"ora %1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d orë"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minuta"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minuta"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Ora"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"për minuta"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"për orë"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-sr/strings.xml b/compose/material3/material3/src/androidMain/res/values-sr/strings.xml
deleted file mode 100644
index 2b0fe74..0000000
--- a/compose/material3/material3/src/androidMain/res/values-sr/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Дијалог"</string>
- <string name="expanded" msgid="5974471714631304645">"Проширено је"</string>
- <string name="collapsed" msgid="5389587048670450460">"Скупљено је"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Одбаците"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Претрага"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Предлози су у наставку"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Изаберите датум"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Изабрани датум"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Пређите на избор године"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Превуците да бисте изабрали годину или додирните да бисте се вратили на избор дана"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Пређите на следећи месец"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Пређите на претходни месец"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Идите на годину: %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Актуелни избор: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Ништа"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Данас"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Видљив бирач година"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Изаберите датум"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Унети датум"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Датум"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Унети датум: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Ништа"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Датум није дозвољен: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Датум не одговара очекиваном шаблону: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Датум је изван очекиваног опсега година %1$s – %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Пређите на режим уноса у Календару"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Пређите на режим уноса текста"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Померајте да би се приказале касније године"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Померајте да би се приказале раније године"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Изаберите датуме"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Датум почетка"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Датум завршетка"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Померајте да би се приказао следећи месец"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Померајте да би се приказао претходни месец"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"У домету"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Унесите датуме"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Унос опсега датума је неважећи"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Идентификатор за превлачење"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Скупи доњу табелу"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Одбаци доњу табелу"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Прошири доњу табелу"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Објашњење"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Прикажи објашњење"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"по"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"пр"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Изаберите пре подне или по подне"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Изаберите сат"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Изаберите минуте"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d ч"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d ч"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d мин"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Минут"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Сат"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"за минуте"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"за сате"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-sv/strings.xml b/compose/material3/material3/src/androidMain/res/values-sv/strings.xml
deleted file mode 100644
index 212db21..0000000
--- a/compose/material3/material3/src/androidMain/res/values-sv/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialogruta"</string>
- <string name="expanded" msgid="5974471714631304645">"Utökad"</string>
- <string name="collapsed" msgid="5389587048670450460">"Komprimerad"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Stäng"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Sök"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Se förslag nedan"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Välj datum"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Valt datum"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Byt till att välja år"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Svep för att välja ett år eller tryck för att återgå till att välja en dag"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Ändra till nästa månad"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Ändra till föregående månad"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Navigera till %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Aktuellt val: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Inget"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"I dag"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Årväljaren är synlig"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Välj datum"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Angivet datum"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Datum"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Angivet datum: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Inget"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Datumet är inte tillåtet: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Datumet matchar inte det förväntade formatet: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Datumet faller utanför det förväntade årsintervallet %1$s–%2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Byt till kalender som inmatningsläge"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Byt till text som inmatningsläge"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Scrolla för att visa senare år"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Scrolla för att visa föregående år"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Välj datum"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Startdatum"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Slutdatum"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Scrolla för att visa nästa månad"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Scrolla för att visa föregående månad"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Inom intervall"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Ange datum"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Ett ogiltigt datumintervall har angetts"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Handtag"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Komprimera arket på nedre delen av skärmen"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Stäng arket på nedre delen av skärmen"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Utöka arket på nedre delen av skärmen"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Beskrivning"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Visa beskrivning"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"EM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"FM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Välj mellan FM och EM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Ange timme"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Välj minuter"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"Klockan %1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d timmar"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minuter"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minut"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Timme"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"för minuter"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"för timmar"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-sw/strings.xml b/compose/material3/material3/src/androidMain/res/values-sw/strings.xml
deleted file mode 100644
index 6aa23aa..0000000
--- a/compose/material3/material3/src/androidMain/res/values-sw/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Mazungumzo"</string>
- <string name="expanded" msgid="5974471714631304645">"Imepanuliwa"</string>
- <string name="collapsed" msgid="5389587048670450460">"Imekunjwa"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Ondoa"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Tafuta"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Mapendekezo yaliyo hapa chini"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Chagua tarehe"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Tarehe uliyochagua"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Nenda kwenye sehemu ya kuchagua mwaka"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Telezesha ili uchague mwaka au gusa ili urejee kwenye kuchagua siku"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Nenda kwenye mwezi unaofuata"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Rudi kwenye mwezi uliotangulia"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Nenda kwenye mwaka %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Iliyochaguliwa sasa: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Hamna"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Leo"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Kiteua mwaka kinaonekana"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Chagua tarehe"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Tarehe iliyowekwa"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Tarehe"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Tarehe iliyowekwa: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Hamna"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Tarehe isiyoruhusiwa: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Tarehe hailingani na mchoro uliotarajiwa: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Tarehe nje ya kipindi cha mwaka kilichotarajiwa %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Badilisha mipangilio ya kuingiza data ya kalenda"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Badilisha kwenda mipangilio ya kuingiza data ya maandishi"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Sogeza ili kuonyesha miaka ya baadaye"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Sogeza ili kuonyesha miaka iliyopita"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Chagua tarehe"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Tarehe ya kuanza"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Tarehe ya mwisho"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Sogeza ili kuonyesha mwezi ujao"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Sogeza ili kuonyesha mwezi uliopita"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Ipo karibu"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Weka tarehe"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Kipindi kilichowekwa si sahihi"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Aikoni ya buruta"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Kunja safu ya chini"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Ondoa safu ya chini"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Panua safu ya chini"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Kidirisha cha vidokezo"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Onyesha kidirisha cha vidokezo"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"Mchana"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"Asubuhi"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Chagua Asubuhi au Mchana"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Chagua saa"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Chagua dakika"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"Saa %1$d kamili"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"Saa %1$d"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"Dakika %1$d"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Dakika"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Saa"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"kwa dakika"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"kwa saa moja"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ta/strings.xml b/compose/material3/material3/src/androidMain/res/values-ta/strings.xml
deleted file mode 100644
index 59cac2f..0000000
--- a/compose/material3/material3/src/androidMain/res/values-ta/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"உரையாடல்"</string>
- <string name="expanded" msgid="5974471714631304645">"விரிக்கப்பட்டது"</string>
- <string name="collapsed" msgid="5389587048670450460">"சுருக்கப்பட்டது"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"மூடும்"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"தேடலாம்"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"பரிந்துரைகள் கீழே கிடைக்கும்"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"தேதியைத் தேர்ந்தெடுக்கவும்"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"தேர்ந்தெடுக்கப்பட்ட தேதி"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"ஆண்டைத் தேர்ந்தெடுக்கும் விருப்பத்திற்கு மாற்று"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"ஆண்டைத் தேர்வுசெய்ய ஸ்வைப் செய்யுங்கள் அல்லது தேதியைத் தேர்வுசெய்யும் பக்கத்திற்கு மீண்டும் செல்ல தட்டுங்கள்"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"அடுத்த மாதத்திற்கு மாற்று"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"முந்தைய மாதத்திற்கு மாற்று"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"%1$sக்குச் செல்லும்"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"தற்போது %1$s தேர்வுசெய்யப்பட்டுள்ளது"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"ஏதுமில்லை"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"இன்று"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"ஆண்டைத் தேர்வுசெய்யும் பக்கம் காட்டப்படுகிறது"</string>
- <string name="date_input_title" msgid="3010396677286327048">"தேதியைத் தேர்வுசெய்யுங்கள்"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"உள்ளிட்ட தேதி"</string>
- <string name="date_input_label" msgid="5194825853981987218">"தேதி"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"உள்ளிட்ட தேதி: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"ஏதுமில்லை"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"இந்தத் தேதி அனுமதிக்கப்படவில்லை: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"எதிர்பார்க்கப்படும் பேட்டர்னுடன் தேதி பொருந்தவில்லை: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"இந்தத் தேதி %1$s - %2$s ஆண்டு வரம்பிற்குள் இல்லை"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"கேலெண்டர் உள்ளீட்டு முறைக்கு மாற்று"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"எழுத்து உள்ளீட்டு முறைக்கு மாற்று"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"பிந்தைய ஆண்டுகளைப் பார்க்க ஸ்க்ரோல் செய்யுங்கள்"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"முந்தைய ஆண்டுகளைப் பார்க்க ஸ்க்ரோல் செய்யுங்கள்"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"தேதிகளைத் தேர்ந்தெடுங்கள்"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"தொடக்கத் தேதி"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"முடிவுத் தேதி"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"அடுத்த மாதத்தைப் பார்க்க ஸ்க்ரோல் செய்யுங்கள்"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"முந்தைய மாதத்தைப் பார்க்க ஸ்க்ரோல் செய்யுங்கள்"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"வரம்பிற்குள் உள்ளது"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"தேதிகளை உள்ளிடுங்கள்"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"தவறான தேதி வரம்பை உள்ளிட்டுள்ளீர்கள்"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"இழுப்பதற்கான ஹேண்டில்"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"கீழ்ப்புறச் சீட்டைச் சுருக்கும்"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"கீழ்ப்புறச் சீட்டை நிராகரிக்கும்"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"கீழ்ப்புறச் சீட்டை விரிவாக்கும்"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"உதவிக்குறிப்பு"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"உதவிக்குறிப்பைக் காட்டு"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"AM/PM என்பதைத் தேர்ந்தெடுக்கலாம்"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"மணிநேரத்தைத் தேர்ந்தெடுக்கலாம்"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"நிமிடங்களைத் தேர்ந்தெடுக்கலாம்"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d மணி"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d மணிநேரம்"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d நிமிடங்கள்"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"நிமிடம்"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"மணிநேரம்"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"நிமிடங்களுக்கு"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"மணிநேரத்திற்கு"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-te/strings.xml b/compose/material3/material3/src/androidMain/res/values-te/strings.xml
deleted file mode 100644
index aab4e0f..0000000
--- a/compose/material3/material3/src/androidMain/res/values-te/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"డైలాగ్"</string>
- <string name="expanded" msgid="5974471714631304645">"విస్తరించబడింది"</string>
- <string name="collapsed" msgid="5389587048670450460">"కుదించబడింది"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"విస్మరించండి"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"సెర్చ్ చేయండి"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"సూచనలు దిగువున ఉన్నాయి"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"తేదీని ఎంచుకోండి"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"ఎంచుకున్న తేదీ"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"ఒక సంవత్సరాన్ని ఎంచుకునే ఆప్షన్కు మారండి"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"సంవత్సరాన్ని ఎంచుకోవడానికి స్వైప్ చేయండి, లేదా రోజును ఎంచుకునేందుకు తిరిగి మారడానికి ట్యాప్ చేయండి"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"వచ్చే నెలకు మార్చండి"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"మునుపటి నెలకు మార్చండి"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"%1$s సంవత్సరానికి వెళ్లండి"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"ప్రస్తుత ఎంపిక: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"ఏదీ లేదు"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"ఈ రోజు"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"సంవత్సరం పికర్ కనిపిస్తుంది"</string>
- <string name="date_input_title" msgid="3010396677286327048">"తేదీ ఎంచుకోండి"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"ఎంటర్ చేసిన తేదీ"</string>
- <string name="date_input_label" msgid="5194825853981987218">"తేదీ"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"ఎంటర్ చేసిన తేదీ: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"ఏదీ లేదు"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"చెల్లని తేదీ: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"తేదీ ఉండాల్సిన ఫార్మాట్తో మ్యాచ్ కాలేదు: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"ఉండాల్సిన సంవత్సరాల పరిధి %1$s - %2$sలో తేదీ లేదు"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"క్యాలెండర్ ఇన్పుట్ మోడ్కు స్విచ్ అవ్వండి"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"టెక్స్ట్ ఇన్పుట్ మోడ్కు స్విచ్ అవ్వండి"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"తదుపరి సంవత్సరాల డేటాను చూడటానికి స్క్రోల్ చేయండి"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"మునుపటి సంవత్సరాల డేటాను చూడటానికి స్క్రోల్ చేయండి"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"తేదీలను ఎంచుకోండి"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"ప్రారంభ తేదీ"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"ముగింపు తేదీ"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"వచ్చే నెల డేటాను చూడటానికి స్క్రోల్ చేయండి"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"మునుపటి నెల డేటాను చూడటానికి స్క్రోల్ చేయండి"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"పరిధిలో ఉంది"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"తేదీలను ఎంటర్ చేయండి"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"తేదీల పరిధి ఇన్పుట్ చెల్లదు"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"లాగే హ్యాండిల్"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"దిగువున ఉన్న షీట్ను కుదిస్తుంది"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"దిగువున ఉన్న షీట్ను విస్మరిస్తుంది"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"దిగువున ఉన్న షీట్ను విస్తరిస్తుంది"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"టూల్టిప్"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"టూల్టిప్ను చూపించు"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"AM లేదా PMను ఎంచుకోండి"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"గంటను ఎంచుకోండి"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"నిమిషాలను ఎంచుకోండి"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d గంటలు"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d గంటలు"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d నిమిషాలు"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"నిమిషం"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"గంట"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"నిమిషాలకు"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ఒక గంట పాటు"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-th/strings.xml b/compose/material3/material3/src/androidMain/res/values-th/strings.xml
deleted file mode 100644
index 5369807..0000000
--- a/compose/material3/material3/src/androidMain/res/values-th/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"กล่องโต้ตอบ"</string>
- <string name="expanded" msgid="5974471714631304645">"ขยาย"</string>
- <string name="collapsed" msgid="5389587048670450460">"ยุบ"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"ปิด"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"ค้นหา"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"มีคำแนะนำที่ด้านล่าง"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"เลือกวันที่"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"วันที่ที่เลือก"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"เปลี่ยนไปที่การเลือกปี"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"ปัดเพื่อเลือกปีหรือแตะเพื่อเปลี่ยนกลับไปยังการเลือกวัน"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"เปลี่ยนไปที่เดือนถัดไป"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"เปลี่ยนไปที่เดือนก่อนหน้า"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"ไปยังปี %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"การเลือกปัจจุบัน: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"ไม่มี"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"วันนี้"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"แสดงตัวเลือกปี"</string>
- <string name="date_input_title" msgid="3010396677286327048">"เลือกวันที่"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"วันที่ป้อน"</string>
- <string name="date_input_label" msgid="5194825853981987218">"วันที่"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"วันที่ป้อน: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"ไม่มี"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"ไม่อนุญาตให้ใช้วันที่นี้: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"วันที่ไม่ตรงกับรูปแบบที่คาดไว้: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"วันที่อยู่นอกเหนือจากช่วงปีที่คาดไว้ %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"สลับไปใช้โหมดป้อนข้อมูลปฏิทิน"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"สลับไปใช้โหมดการป้อนข้อความ"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"เลื่อนเพื่อแสดงปีหลังจากนี้"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"เลื่อนเพื่อแสดงปีก่อนหน้านี้"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"เลือกวันที่"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"วันที่เริ่มต้น"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"วันที่สิ้นสุด"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"เลื่อนเพื่อแสดงเดือนถัดไป"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"เลื่อนเพื่อแสดงเดือนก่อนหน้า"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"อยู่ในช่วงวันที่ที่เลือก"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"ป้อนวันที่"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"การป้อนข้อมูลช่วงวันที่ไม่ถูกต้อง"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"แฮนเดิลการลาก"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"ยุบ Bottom Sheet"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ปิด Bottom Sheet"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ขยาย Bottom Sheet"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"เคล็ดลับเครื่องมือ"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"แสดงเคล็ดลับเครื่องมือ"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"เลือก AM หรือ PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"เลือกชั่วโมง"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"เลือกนาที"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d นาฬิกา"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d ชั่วโมง"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d นาที"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"นาที"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"ชั่วโมง"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"สำหรับนาที"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"สำหรับชั่วโมง"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-tl/strings.xml b/compose/material3/material3/src/androidMain/res/values-tl/strings.xml
deleted file mode 100644
index cdbe629..0000000
--- a/compose/material3/material3/src/androidMain/res/values-tl/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Dialog"</string>
- <string name="expanded" msgid="5974471714631304645">"Naka-expand"</string>
- <string name="collapsed" msgid="5389587048670450460">"Naka-collapse"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"I-dismiss"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Maghanap"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Mga suhestyon sa ibaba"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Pumili ng petsa"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Piniling petsa"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Lumipat sa pagpili ng taon"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Mag-swipe para pumili ng taon, o mag-tap para bumalik sa pagpili ng araw"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Lumipat sa susunod na buwan"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Lumipat sa nakaraang buwan"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Mag-navigate papunta sa taong %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Kasalukuyang napili: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Wala"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Ngayon"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Nakikita ang picker ng taon"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Pumili ng petsa"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Inilagay na petsa"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Petsa"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Inilagay na petsa: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Wala"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Hindi pinapayagan ang petsa: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Hindi tumutugma ang petsa sa inaasahang pattern: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Wala ang petsa sa inaasahang hanay ng taon na %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Lumipat sa pamamaraan ng pag-input ng kalendaryo"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Lumipat sa pamamaraan ng pag-input ng text"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Mag-scroll para makita ang mga mas kamakailang taon"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Mag-scroll para ipakita ang mga mas naunang taon"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Pumili ng mga petsa"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Petsa ng pagsisimula"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Petsa ng pagtatapos"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Mag-scroll para ipakita ang susunod na buwan"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Mag-scroll para ipakita ang nakaraang buwan"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"May signal"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Maglagay ng mga petsa"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Invalid ang input na hanay ng petsa"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Handle sa pag-drag"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"I-collapse ang bottom sheet"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"I-dismiss ang bottom sheet"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Palawakin ang bottom sheet"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Tooltip"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Ipakita ang tooltip"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Piliin ang AM o PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Pumili ng oras"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Pumili ng mga minuto"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d o\'clock"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d oras"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d (na) minuto"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Minuto"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Oras"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"nang ilang minuto"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"nang ilang oras"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-tr/strings.xml b/compose/material3/material3/src/androidMain/res/values-tr/strings.xml
deleted file mode 100644
index 795e820..0000000
--- a/compose/material3/material3/src/androidMain/res/values-tr/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"İletişim kutusu"</string>
- <string name="expanded" msgid="5974471714631304645">"Genişletilmiş"</string>
- <string name="collapsed" msgid="5389587048670450460">"Daraltılmış"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Kapat"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Arama"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Önerileri aşağıda bulabilirsiniz"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Tarih seç"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Seçilen tarih"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Yıl seçimine geç"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Kaydırarak bir yıl seçin veya gün seçme bölümüne geri dönmek için dokunun"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Sonraki aya değiştir"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Önceki aya değiştir"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"%1$s yılına gidin"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Geçerli seçim: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Yok"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Bugün"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Yıl seçici görünür durumda"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Tarih seç"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Girilen tarih"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Tarih"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Girilen tarih: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Yok"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Tarihe izin verilmiyor: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Tarih, istenen biçimle eşleşmiyor: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Tarih, istenen %1$s - %2$s yıl aralığının dışında"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Takvim giriş moduna geç"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Metin giriş moduna geç"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Sonraki yılları göstermek için kaydırın"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Önceki yılları göstermek için kaydırın"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Tarihleri seçin"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Başlangıç tarihi"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Bitiş tarihi"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Sonraki ayı göstermek için kaydırın"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Önceki ayı göstermek için kaydırın"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Aralıkta"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Tarihleri girin"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Geçersiz tarih aralığı girişi"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Sürükleme tutamacı"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Alt sayfayı daralt"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Alt sayfayı kapat"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Alt sayfayı genişlet"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"İpucu"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Araç ipucunu göster"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"ÖS"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"ÖÖ veya ÖS\'yi seçin"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Saat seçin"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Dakikayı seçin"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"Saat %1$d"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d saat"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d dakika"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Dakika"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Saat"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"dakika"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"saat"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-uk/strings.xml b/compose/material3/material3/src/androidMain/res/values-uk/strings.xml
deleted file mode 100644
index 46376ba..0000000
--- a/compose/material3/material3/src/androidMain/res/values-uk/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Вікно"</string>
- <string name="expanded" msgid="5974471714631304645">"Розгорнуто"</string>
- <string name="collapsed" msgid="5389587048670450460">"Згорнуто"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Закрити"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Пошук"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Підказки внизу"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Вибрати дату"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Вибрана дата"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Перейти до вибору року"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Проведіть пальцем по екрану, щоб вибрати рік, або торкніться, щоб повернутися до вибору дня"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Перейти до наступного місяця"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Перейти до попереднього місяця"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Перейти до %1$s року"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Поточний вибір: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Немає"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Сьогодні"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Показувати засіб вибору року"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Виберіть дату"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Введена дата"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Дата"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Введена дата: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Немає"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Недопустима дата: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Дата не відповідає очікуваному шаблону: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Дата за межами очікуваного діапазону років %1$s – %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Перейти в режим введення в календарі"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Перейти в режим введення тексту"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Прокрутіть, щоб відобразити пізніші роки"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Прокрутіть, щоб відобразити попередні роки"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Виберіть дати"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Дата початку"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Дата завершення"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Прокрутіть, щоб відобразити наступний місяць"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Прокрутіть, щоб відобразити попередній місяць"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"У діапазоні"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Введіть дати"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Указано недійсний діапазон дат"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Маркер переміщення"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Згорнути нижній екран"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Закрити нижній екран"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Розгорнути нижній екран"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Спливаюча підказка"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Показати підказку"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"ПП"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"ДП"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Виберіть ДП чи ПП"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Вибрати годину"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Вибрати хвилини"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d год"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d год"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d хв"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Хвилина"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Година"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"для хвилин"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"для годин"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ur/strings.xml b/compose/material3/material3/src/androidMain/res/values-ur/strings.xml
deleted file mode 100644
index b2bbeaf..0000000
--- a/compose/material3/material3/src/androidMain/res/values-ur/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"ڈائلاگ"</string>
- <string name="expanded" msgid="5974471714631304645">"پھیلایا گیا"</string>
- <string name="collapsed" msgid="5389587048670450460">"سکیڑا گیا"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"برخاست کریں"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"تلاش کریں"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"تلاش کی تجاویز نیچے دستیاب ہیں"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"تاریخ منتخب کریں"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"منتخب کردہ تاریخ"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"ایک سال کا انتخاب کرنے کے لیے سوئچ کریں"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"ایک سال منتخب کرنے کے لیے سوائپ کریں یا ایک دن منتخب کرنے کی خاطر دوبارہ سوئچ کرنے کے لیے تھپتھپائیں"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"اگلے ماہ میں تبدیل کریں"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"گزشتہ ماہ میں منتقل کریں"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"سال %1$s پر نیویگیٹ کریں"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"موجودہ انتخاب: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"کوئی نہیں"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"آج"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"سال کا منتخب کنندہ مرئی ہے"</string>
- <string name="date_input_title" msgid="3010396677286327048">"تاریخ منتخب کریں"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"درج کردہ تاریخ"</string>
- <string name="date_input_label" msgid="5194825853981987218">"تاریخ"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"درج کردہ تاریخ: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"کوئی نہیں"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"تاریخ کی اجازت نہیں ہے: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"تاریخ متوقع پیٹرن سے مماثل نہیں ہے: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"متوقع سال کی حد %1$s - %2$s سے باہر کی تاریخ"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"کیلنڈر اندراج کے طرز پر سوئچ کریں"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"ٹیکسٹ اندراج کے طرز پر سوئچ کریں"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"بعد کے سالوں کا ڈیٹا دکھانے کے لیے اسکرول کریں"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"پچھلے سالوں کا ڈیٹا دکھانے کے لیے اسکرول کریں"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"تواریخ منتخب کریں"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"تاریخ آغاز"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"تاریخ اختتام"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"اگلے مہینے کا ڈیٹا دکھانے کے لیے اسکرول کریں"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"پچھلے مہینے کا ڈیٹا دکھانے کے لیے اسکرول کریں"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"رینج میں ہے"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"تواریخ درج کریں"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"تاریخ کی حد کا غلط ان پٹ"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"گھسیٹنے کا ہینڈل"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"نیچے کی شیٹ کو سکیڑیں"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"نیچے کی شیٹ کو برخاست کریں"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"نیچے کی شیٹ کو پھیلائیں"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"ٹول ٹپ"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"ٹول ٹپ دکھائیں"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"بعد از دوپہر"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"قبلاز دوپہر"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"قبل از دوپہر یا بعد از دوپہر منتخب کریں"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"گھنٹہ منتخب کریں"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"منٹس منتخب کریں"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d بجے"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d گھنٹے"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d منٹس"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"منٹ"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"گھنٹہ"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"منٹ کے لیے"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"گھنٹے کے لیے"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-uz/strings.xml b/compose/material3/material3/src/androidMain/res/values-uz/strings.xml
deleted file mode 100644
index 489ed71..0000000
--- a/compose/material3/material3/src/androidMain/res/values-uz/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Muloqot oynasi"</string>
- <string name="expanded" msgid="5974471714631304645">"Yoyilgan"</string>
- <string name="collapsed" msgid="5389587048670450460">"Yigʻilgan"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Yopish"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Qidiruv"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Takliflar quyida"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Sanani tanlang"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Tanlangan sana"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Tanlangan yilga oʻtish"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Yilni tanlash uchun suring yoki kunni tanlashga qaytish uchun tegining"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Keyingi oyga oʻzgartirish"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Avvalgi oyga oʻzgartirish"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"%1$s-yilga oʻtish"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Joriy tanlov: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Hech biri"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Bugun"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Yil tanlagich ochiq"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Sanani tanlang"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Kiritilgan sana"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Sana"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Kiritilgan sana: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Yoʻq"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Sana xato: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Sana mavjud namunaga mos kelmaydi: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Sana kutilgan yil oraligʻida emas: %1$s – %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Taqvim kiritish rejimiga oʻtish"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Matn kiritish rejimiga oʻtish"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Keyingi yillarga varaqlang"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Avvalgi yillarga varaqlang"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Sanalarni tanlang"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Boshlanish sanasi"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Tugash sanasi"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Keyingi oyna varaqlang"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Avvalgi oyga varaqlang"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Xizmat doirasida"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Sanalarni kiriting"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Kiritilgan muddat yaroqsiz"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Surish dastagi"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Quyi ekranni yigʻish"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Quyi ekranni yopish"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Quyi ekranni yoyish"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Maslahat oynasi"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Maslahat oynasini koʻrsatish"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"TK"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"TO"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Tushdan oldin yoki keyinligini tanlang"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Soatni tanlang"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Daqiqani tanlang"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d soat"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d soat"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d daqiqa"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Daqiqa"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Soat"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"bir daqiqa"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"bir soat"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-vi/strings.xml b/compose/material3/material3/src/androidMain/res/values-vi/strings.xml
deleted file mode 100644
index 59d238e0..0000000
--- a/compose/material3/material3/src/androidMain/res/values-vi/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Hộp thoại"</string>
- <string name="expanded" msgid="5974471714631304645">"Đã mở rộng"</string>
- <string name="collapsed" msgid="5389587048670450460">"Đã thu gọn"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Đóng"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Tìm kiếm"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Các đề xuất ở bên dưới"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Chọn ngày"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Ngày đã chọn"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Chuyển sang chọn năm"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Vuốt để chọn một năm hoặc nhấn để chuyển lại về chọn một ngày"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Chuyển sang tháng tiếp theo"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Chuyển về tháng trước"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Chuyển đến năm %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Lựa chọn hiện tại: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Không có"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Hôm nay"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Bộ chọn năm hiển thị"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Chọn ngày"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Ngày đã nhập"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Ngày"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Ngày đã nhập: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Không có"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Ngày không được phép: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Ngày không khớp với định dạng dự kiến: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Ngày không thuộc phạm vi năm dự kiến %1$s – %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Chuyển sang chế độ nhập lịch"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Chuyển sang chế độ nhập văn bản"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Cuộn để hiện những năm sau"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Cuộn để hiện những năm trước"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Chọn ngày"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Ngày bắt đầu"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Ngày kết thúc"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Cuộn để hiện tháng sau"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Cuộn để hiện tháng trước"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Trong khoảng"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Nhập ngày"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Phạm vi ngày đã nhập không hợp lệ"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Nút kéo"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Thu gọn bảng dưới cùng"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Bỏ qua bảng dưới cùng"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Mở rộng bảng dưới cùng"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Chú giải công cụ"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Hiển thị chú giải công cụ"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"CH"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"SA"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Chọn SA hoặc CH"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Chọn giờ"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Chọn phút"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d giờ"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d giờ"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d phút"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Phút"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Giờ"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"về số phút"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"về số giờ"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-zh-rCN/strings.xml b/compose/material3/material3/src/androidMain/res/values-zh-rCN/strings.xml
deleted file mode 100644
index f2e1a9b..0000000
--- a/compose/material3/material3/src/androidMain/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"对话框"</string>
- <string name="expanded" msgid="5974471714631304645">"已展开"</string>
- <string name="collapsed" msgid="5389587048670450460">"已收起"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"关闭"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"搜索"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"以下是搜索建议"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"选择日期"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"选定的日期"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"切换以选择年份"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"滑动可选择年份,点按可切换回选择日期"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"转到下个月"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"转到上个月"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"切换到年份:%1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"当前的选择:%1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"无"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"今天"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"年份选择器可见"</string>
- <string name="date_input_title" msgid="3010396677286327048">"选择日期"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"输入日期"</string>
- <string name="date_input_label" msgid="5194825853981987218">"日期"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"输入日期:%1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"无"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"不允许的日期:%1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"日期不符合预期格式:%1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"日期超出预期年份范围 %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"切换到日历输入模式"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"切换到文本字段输入模式"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"滚动显示之后的年份"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"滚动显示之前的年份"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"选择日期"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"开始日期"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"结束日期"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"滚动显示下个月"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"滚动显示上个月"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"在范围内"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"输入日期"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"输入的日期范围无效"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"拖动手柄"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"收起底部动作条"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"关闭底部动作条"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"展开底部动作条"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"提示"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"显示提示"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"下午"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"上午"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"选择上午或下午"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"选择小时"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"选择分钟"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d 点"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d 小时"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d 分钟"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"分"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"时"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"表示分钟"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"表示小时"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-zh-rHK/strings.xml b/compose/material3/material3/src/androidMain/res/values-zh-rHK/strings.xml
deleted file mode 100644
index 939c4ca..0000000
--- a/compose/material3/material3/src/androidMain/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"對話框"</string>
- <string name="expanded" msgid="5974471714631304645">"展開咗"</string>
- <string name="collapsed" msgid="5389587048670450460">"合埋咗"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"閂"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"搵"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"建議如下"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"選取日期"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"所選日期"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"切換為選取年份"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"滑動可選取年分,或可輕按返回選取日期"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"變更至下個月"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"變更至上個月"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"前往 %1$s 年"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"目前選項:%1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"無"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"今天"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"顯示年分挑選器"</string>
- <string name="date_input_title" msgid="3010396677286327048">"選取日期"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"已輸入的日期"</string>
- <string name="date_input_label" msgid="5194825853981987218">"日期"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"已輸入的日期:%1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"無"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"不允許的日期:%1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"日期格式不符:%1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"日期超出預期的年份範圍:%1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"切換至日曆輸入模式"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"切換至文字輸入模式"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"碌去顯示新啲嘅年份"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"碌去顯示舊啲嘅年份"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"選取日期"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"開始日期"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"結束日期"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"碌去顯示下一個月"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"碌去顯示上一個月"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"在指定日期範圍內"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"輸入日期"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"輸入的日期範圍無效"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"拖曳控點"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"收合底部功能表"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"關閉底部功能表"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"展開底部功能表"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"提示"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"顯示提示"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"下午"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"上午"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"選取上午或下午"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"選取小時"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"選取分鐘"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d 點"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d 點"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d 分鐘"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"分鐘"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"小時"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"分鐘"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"小時"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-zh-rTW/strings.xml b/compose/material3/material3/src/androidMain/res/values-zh-rTW/strings.xml
deleted file mode 100644
index b17c4cf..0000000
--- a/compose/material3/material3/src/androidMain/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"對話方塊"</string>
- <string name="expanded" msgid="5974471714631304645">"已展開"</string>
- <string name="collapsed" msgid="5389587048670450460">"已收合"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"關閉"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"搜尋"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"建議如下"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"選取日期"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"所選日期"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"改為選取年份"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"滑動可選取年分,也可輕觸返回選取日期"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"改成下個月"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"改成上個月"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"前往 %1$s 年"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"目前選項:%1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"無"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"今天"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"顯示年份挑選器"</string>
- <string name="date_input_title" msgid="3010396677286327048">"選取日期"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"輸入的日期"</string>
- <string name="date_input_label" msgid="5194825853981987218">"日期"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"輸入的日期:%1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"無"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"不允許的日期:%1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"日期格式不符:%1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"日期超出預期的年份範圍:%1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"切換至日曆輸入模式"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"切換至文字輸入模式"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"捲動即可顯示之後的年分"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"捲動即可顯示先前的年分"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"選取日期"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"開始日期"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"結束日期"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"捲動即可顯示下一個月"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"捲動即可顯示上一個月"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"在指定日期範圍內"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"輸入日期"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"輸入的日期範圍無效"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"拖曳控點"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"收合底部功能表"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"關閉底部功能表"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"展開底部功能表"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"工具提示"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"顯示工具提示"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"下午"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"上午"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"選取上午或下午"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"選取小時"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"選取分鐘"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d 點"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"%1$d 時"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d 分鐘"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"分鐘"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"小時"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"分鐘"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"小時"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-zu/strings.xml b/compose/material3/material3/src/androidMain/res/values-zu/strings.xml
deleted file mode 100644
index d1f05ad..0000000
--- a/compose/material3/material3/src/androidMain/res/values-zu/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2022 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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="dialog" msgid="4057925834421392736">"Ibhokisi"</string>
- <string name="expanded" msgid="5974471714631304645">"Kunwetshiwe"</string>
- <string name="collapsed" msgid="5389587048670450460">"Kugoqiwe"</string>
- <string name="snackbar_dismiss" msgid="3962933905051144957">"Chitha"</string>
- <string name="search_bar_search" msgid="6420018528474762666">"Sesha"</string>
- <string name="suggestions_available" msgid="7189888345201419934">"Iziphakamiso ngezansi"</string>
- <string name="date_picker_title" msgid="9208721003668059792">"Khetha usuku"</string>
- <string name="date_picker_headline" msgid="2846784065735639969">"Khetha usuku"</string>
- <string name="date_picker_switch_to_year_selection" msgid="3412370019845183965">"Shintshela ekukhetheni unyaka"</string>
- <string name="date_picker_switch_to_day_selection" msgid="145089358343568971">"Swayipha ukuze ukhethe unyaka, noma thepha ukuze ubuyele ekukhetheni usuku"</string>
- <string name="date_picker_switch_to_next_month" msgid="8313783187901412102">"Shintshela kunyanga elandelayo"</string>
- <string name="date_picker_switch_to_previous_month" msgid="7596294429748914881">"Shintshela kunyanga edlule"</string>
- <string name="date_picker_navigate_to_year_description" msgid="5152441868029453612">"Funa kunyaka %1$s"</string>
- <string name="date_picker_headline_description" msgid="4627306862713137085">"Ukukhetha kwamanje: %1$s"</string>
- <string name="date_picker_no_selection_description" msgid="5724377114289981899">"Lutho"</string>
- <string name="date_picker_today_description" msgid="4775802721403526937">"Namuhla"</string>
- <string name="date_picker_year_picker_pane_title" msgid="8140324713311804736">"Isikhethi sonyaka siyabonakala"</string>
- <string name="date_input_title" msgid="3010396677286327048">"Khetha usuku"</string>
- <string name="date_input_headline" msgid="3499643850558715142">"Usuku olufakiwe"</string>
- <string name="date_input_label" msgid="5194825853981987218">"Usuku"</string>
- <string name="date_input_headline_description" msgid="8562356184193964298">"Usuku olufakiwe: %1$s"</string>
- <string name="date_input_no_input_description" msgid="5722931102250207748">"Lutho"</string>
- <string name="date_input_invalid_not_allowed" msgid="6114792992433444995">"Usuku aluvunyelwe: %1$s"</string>
- <string name="date_input_invalid_for_pattern" msgid="5281836720766682161">"Usuku alufani nephethini elindelekile: %1$s"</string>
- <string name="date_input_invalid_year_range" msgid="8434112129235255568">"Usuku lungaphandle kwebanga lonyaka elilindelekile %1$s - %2$s"</string>
- <string name="date_picker_switch_to_calendar_mode" msgid="9029369254443419167">"Shintshela kwimodi yokufaka yekhalenda"</string>
- <string name="date_picker_switch_to_input_mode" msgid="1496750567914156598">"Shintshela kwimodi yokufaka yombhalo"</string>
- <string name="date_picker_scroll_to_later_years" msgid="3226341140390493774">"Skrola ukuze ubonise iminyaka yakamuva"</string>
- <string name="date_picker_scroll_to_earlier_years" msgid="8911933542023210271">"Skrola ukuze ubonise iminyaka yangaphambilini"</string>
- <string name="date_range_picker_title" msgid="690080476639943577">"Khetha izinsuku"</string>
- <string name="date_range_picker_start_headline" msgid="5759491386723090559">"Usuku lokuqala"</string>
- <string name="date_range_picker_end_headline" msgid="4766270708882012148">"Usuku lokuphela"</string>
- <string name="date_range_picker_scroll_to_next_month" msgid="51495506931835470">"Skrola ukuze ubonise inyanga elandelayo"</string>
- <string name="date_range_picker_scroll_to_previous_month" msgid="4371570854614540700">"Skrola ukuze ubonise inyanga edlule"</string>
- <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Kubanga"</string>
- <string name="date_range_input_title" msgid="2366412111888449406">"Faka izinsuku"</string>
- <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Okokufaka kwebanga losuku okungavumelekile"</string>
- <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Hudula isibambi"</string>
- <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Goqa ishidi eliphansi"</string>
- <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Chitha ishidi eliphansi"</string>
- <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Nweba ishidi eliphansi"</string>
- <string name="tooltip_pane_description" msgid="8191239805703103845">"Ithulithiphu"</string>
- <string name="tooltip_long_press_label" msgid="2732804537909054941">"Bonisa ithulithiphu"</string>
- <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
- <string name="time_picker_am" msgid="5096144640014509074">"AM"</string>
- <string name="time_picker_period_toggle_description" msgid="7352665290700284516">"Khetha u-AM noma u-PM"</string>
- <string name="time_picker_hour_selection" msgid="6081676287789101196">"Khetha ihora"</string>
- <string name="time_picker_minute_selection" msgid="8494777394375441602">"Khetha imizuzu"</string>
- <string name="time_picker_hour_suffix" msgid="6952032626122080528">"%1$d o\'clock"</string>
- <string name="time_picker_hour_24h_suffix" msgid="4149641012513526783">"Amahora angu-%1$d"</string>
- <string name="time_picker_minute_suffix" msgid="3206486707779478173">"Imizuzu engu-%1$d"</string>
- <string name="time_picker_minute" msgid="6116528647594005945">"Umzuzu"</string>
- <string name="time_picker_hour" msgid="7241191970823415723">"Ihora"</string>
- <string name="time_picker_minute_text_field" msgid="994099543833979061">"ngemizuzu"</string>
- <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ngehora"</string>
-</resources>
diff --git a/compose/material3/material3/src/androidMain/res/values/strings.xml b/compose/material3/material3/src/androidMain/res/values/strings.xml
index dbd7ceb..a2c68b0 100644
--- a/compose/material3/material3/src/androidMain/res/values/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values/strings.xml
@@ -17,94 +17,94 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Spoken description of a dialog -->
- <string name="dialog">"Dialog"</string>
- <!-- Spoken description of expanded state of an expandable item -->
- <string name="expanded">Expanded</string>
- <!-- Spoken description of collapsed state of an expandable item -->
- <string name="collapsed">Collapsed</string>
+ <string name="m3c_dialog">"Dialog"</string>
+ <!-- Spoken description of expanded state of a dropdown menu -->
+ <string name="m3c_dropdown_menu_expanded">Expanded</string>
+ <!-- Spoken description of collapsed state of a dropdown menu -->
+ <string name="m3c_dropdown_menu_collapsed">Collapsed</string>
<!-- Spoken description of a snackbar dismiss action -->
- <string name="snackbar_dismiss">Dismiss</string>
+ <string name="m3c_snackbar_dismiss">Dismiss</string>
<!-- Spoken description of a search bar -->
- <string name="search_bar_search">Search</string>
+ <string name="m3c_search_bar_search">Search</string>
<!-- Spoken description when search suggestions are available -->
- <string name="suggestions_available">Suggestions below</string>
+ <string name="m3c_suggestions_available">Suggestions below</string>
<!-- Spoken description of date picker and date input items -->
- <string name="date_picker_title">"Select date"</string>
- <string name="date_picker_headline">"Selected date"</string>
- <string name="date_picker_switch_to_year_selection">"Switch to selecting a year"</string>
- <string name="date_picker_switch_to_day_selection">
+ <string name="m3c_date_picker_title">"Select date"</string>
+ <string name="m3c_date_picker_headline">"Selected date"</string>
+ <string name="m3c_date_picker_switch_to_year_selection">"Switch to selecting a year"</string>
+ <string name="m3c_date_picker_switch_to_day_selection">
"Swipe to select a year, or tap to switch back to selecting a day"
</string>
- <string name="date_picker_switch_to_next_month">"Change to next month"</string>
- <string name="date_picker_switch_to_previous_month">"Change to previous month"</string>
- <string name="date_picker_navigate_to_year_description">Navigate to year %1$s</string>
- <string name="date_picker_headline_description">Current selection: %1$s</string>
- <string name="date_picker_no_selection_description">None</string>
- <string name="date_picker_today_description">Today</string>
- <string name="date_picker_year_picker_pane_title">Year picker visible</string>
- <string name="date_input_title">Select date</string>
- <string name="date_input_headline">Entered date</string>
- <string name="date_input_label">Date</string>
- <string name="date_input_headline_description">Entered date: %1$s</string>
- <string name="date_input_no_input_description">None</string>
- <string name="date_input_invalid_not_allowed">Date not allowed: %1$s</string>
- <string name="date_input_invalid_for_pattern">Date does not match expected pattern: %1$s</string>
- <string name="date_input_invalid_year_range">
+ <string name="m3c_date_picker_switch_to_next_month">"Change to next month"</string>
+ <string name="m3c_date_picker_switch_to_previous_month">"Change to previous month"</string>
+ <string name="m3c_date_picker_navigate_to_year_description">Navigate to year %1$s</string>
+ <string name="m3c_date_picker_headline_description">Current selection: %1$s</string>
+ <string name="m3c_date_picker_no_selection_description">None</string>
+ <string name="m3c_date_picker_today_description">Today</string>
+ <string name="m3c_date_picker_year_picker_pane_title">Year picker visible</string>
+ <string name="m3c_date_input_title">Select date</string>
+ <string name="m3c_date_input_headline">Entered date</string>
+ <string name="m3c_date_input_label">Date</string>
+ <string name="m3c_date_input_headline_description">Entered date: %1$s</string>
+ <string name="m3c_date_input_no_input_description">None</string>
+ <string name="m3c_date_input_invalid_not_allowed">Date not allowed: %1$s</string>
+ <string name="m3c_date_input_invalid_for_pattern">Date does not match expected pattern: %1$s</string>
+ <string name="m3c_date_input_invalid_year_range">
Date out of expected year range %1$s - %2$s
</string>
- <string name="date_picker_switch_to_calendar_mode">Switch to calendar input mode</string>
- <string name="date_picker_switch_to_input_mode">Switch to text input mode</string>
+ <string name="m3c_date_picker_switch_to_calendar_mode">Switch to calendar input mode</string>
+ <string name="m3c_date_picker_switch_to_input_mode">Switch to text input mode</string>
<!-- Spoken description for scrolling to display later years -->
- <string name="date_picker_scroll_to_later_years">Scroll to show later years</string>
+ <string name="m3c_date_picker_scroll_to_later_years">Scroll to show later years</string>
<!-- Spoken description for scrolling to display earlier years -->
- <string name="date_picker_scroll_to_earlier_years">Scroll to show earlier years</string>
- <string name="date_range_picker_title">Select dates</string>
- <string name="date_range_picker_start_headline">Start date</string>
- <string name="date_range_picker_end_headline">End date</string>
+ <string name="m3c_date_picker_scroll_to_earlier_years">Scroll to show earlier years</string>
+ <string name="m3c_date_range_picker_title">Select dates</string>
+ <string name="m3c_date_range_picker_start_headline">Start date</string>
+ <string name="m3c_date_range_picker_end_headline">End date</string>
<!-- Spoken description for scrolling to the next month -->
- <string name="date_range_picker_scroll_to_next_month">Scroll to show the next month</string>
+ <string name="m3c_date_range_picker_scroll_to_next_month">Scroll to show the next month</string>
<!-- Spoken description for scrolling to the previous month -->
- <string name="date_range_picker_scroll_to_previous_month">Scroll to show the previous month</string>
+ <string name="m3c_date_range_picker_scroll_to_previous_month">Scroll to show the previous month</string>
<!-- Spoken description for a selected day that is within a range of selected days -->
- <string name="date_range_picker_day_in_range">In range</string>
- <string name="date_range_input_title">Enter dates</string>
+ <string name="m3c_date_range_picker_day_in_range">In range</string>
+ <string name="m3c_date_range_input_title">Enter dates</string>
<!--
Describes an invalid date range input when a user enters a start or end date [CHAR_LIMIT=NONE]
-->
- <string name="date_range_input_invalid_range_input">Invalid date range input</string>
+ <string name="m3c_date_range_input_invalid_range_input">Invalid date range input</string>
<!-- Names the drag handle visual for bottom sheet. -->
- <string name="bottom_sheet_drag_handle_description">Drag handle</string>
+ <string name="m3c_bottom_sheet_drag_handle_description">Drag handle</string>
<!-- Describes the collapse action for bottom sheet. -->
- <string name="bottom_sheet_collapse_description">Collapse bottom sheet</string>
+ <string name="m3c_bottom_sheet_collapse_description">Collapse bottom sheet</string>
<!-- Describes the dismiss action for bottom sheet. -->
- <string name="bottom_sheet_dismiss_description">Dismiss bottom sheet</string>
+ <string name="m3c_bottom_sheet_dismiss_description">Dismiss bottom sheet</string>
<!-- Describes the expand action visual for bottom sheet. -->
- <string name="bottom_sheet_expand_description">Expand bottom sheet</string>
+ <string name="m3c_bottom_sheet_expand_description">Expand bottom sheet</string>
<!-- Spoken description of a tooltip -->
- <string name="tooltip_pane_description">Tooltip</string>
- <string name="tooltip_long_press_label">Show tooltip</string>
+ <string name="m3c_tooltip_pane_description">Tooltip</string>
+ <string name="m3c_tooltip_long_press_label">Show tooltip</string>
<!-- Suffix for time in 12-hour standard, after noon. [CHAR_LIMIT=2]" -->
- <string name="time_picker_pm">PM</string>
+ <string name="m3c_time_picker_pm">PM</string>
<!-- Suffix for time in 12-hour standard, before noon. [CHAR_LIMIT=2]" -->
- <string name="time_picker_am">AM</string>
+ <string name="m3c_time_picker_am">AM</string>
<!-- Description for the toggle to choose between AM and PM [CHAR_LIMIT=NONE] -->
- <string name="time_picker_period_toggle_description">Select AM or PM</string>
+ <string name="m3c_time_picker_period_toggle_description">Select AM or PM</string>
<!-- Description for button to switch to select the hour [CHAR_LIMIT=NONE] -->
- <string name="time_picker_hour_selection">Select hour</string>
+ <string name="m3c_time_picker_hour_selection">Select hour</string>
<!-- Description for button to switch to select the minute [CHAR_LIMIT=NONE] -->
- <string name="time_picker_minute_selection">Select minutes</string>
+ <string name="m3c_time_picker_minute_selection">Select minutes</string>
<!-- Spoken suffix for an hour in the clock [CHAR_LIMIT=10] -->
- <string name="time_picker_hour_suffix">%1$d o\'clock</string>
+ <string name="m3c_time_picker_hour_suffix">%1$d o\'clock</string>
<!-- Spoken suffix for an hour in the 24-hour clock [CHAR_LIMIT=10] -->
- <string name="time_picker_hour_24h_suffix">%1$d hours</string>
+ <string name="m3c_time_picker_hour_24h_suffix">%1$d hours</string>
<!-- Spoken suffix for an amount of minutes in the clock [CHAR_LIMIT=16] -->
- <string name="time_picker_minute_suffix">%1$d minutes</string>
+ <string name="m3c_time_picker_minute_suffix">%1$d minutes</string>
<!-- Label for a textField that allows the user to enter minutes to set the time -->
- <string name="time_picker_minute">Minute</string>
+ <string name="m3c_time_picker_minute">Minute</string>
<!-- Label for a textField that allows the user to the hour to set the time -->
- <string name="time_picker_hour">Hour</string>
+ <string name="m3c_time_picker_hour">Hour</string>
<!-- A label for a textfield that allows the user to input minutes. It reads: Edit Box, for minutes -->
- <string name="time_picker_minute_text_field">for minutes</string>
+ <string name="m3c_time_picker_minute_text_field">for minutes</string>
<!-- A label for a textfield that allows the user to input hours. It reads: Edit Box, for hour -->
- <string name="time_picker_hour_text_field">for hour</string>
+ <string name="m3c_time_picker_hour_text_field">for hour</string>
</resources>
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AlertDialog.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AlertDialog.kt
index 21cd441..62a0006 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AlertDialog.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AlertDialog.kt
@@ -149,7 +149,8 @@
if (sequences.isNotEmpty()) {
crossAxisSpace += crossAxisSpacing.roundToPx()
}
- sequences += currentSequence.toList()
+ // Ensures that confirming actions appear above dismissive actions.
+ sequences.add(0, currentSequence.toList())
crossAxisSizes += currentCrossAxisSize
crossAxisPositions += crossAxisSpace
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
index 0ff7c120..4b558c6 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
@@ -21,6 +21,7 @@
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
+import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
@@ -34,7 +35,6 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.tokens.MenuTokens
@@ -61,11 +61,11 @@
import kotlin.math.max
import kotlin.math.min
-@Suppress("ModifierParameter")
@Composable
internal fun DropdownMenuContent(
expandedStates: MutableTransitionState<Boolean>,
transformOriginState: MutableState<TransformOrigin>,
+ scrollState: ScrollState,
modifier: Modifier = Modifier,
content: @Composable ColumnScope.() -> Unit
) {
@@ -133,7 +133,7 @@
modifier = modifier
.padding(vertical = DropdownMenuVerticalPadding)
.width(IntrinsicSize.Max)
- .verticalScroll(rememberScrollState()),
+ .verticalScroll(scrollState),
content = content
)
}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
index 2746436..5f1bf664 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
@@ -535,8 +535,8 @@
* [animationProgress].
*
* When [alwaysShowLabel] is true, the positions do not move. The [iconPlaceable] will be placed
- * near the top of the item and the [labelPlaceable] will be placed near the bottom, according to
- * the spec.
+ * near the top of the item and the [labelPlaceable] will be placed beneath it with padding,
+ * according to the spec.
*
* When [animationProgress] is 1 (representing the selected state), the positions will be the same
* as above.
@@ -573,11 +573,13 @@
): MeasureResult {
val height = constraints.maxHeight
- // Label should be `ItemVerticalPadding` from the bottom
- val labelY = height - labelPlaceable.height - NavigationBarItemVerticalPadding.roundToPx()
+ val contentTotalHeight = iconPlaceable.height + IndicatorVerticalPadding.roundToPx() +
+ NavigationBarIndicatorToLabelPadding.roundToPx() + labelPlaceable.height
+ val contentVerticalPadding = ((height - contentTotalHeight) / 2)
+ .coerceAtLeast(IndicatorVerticalPadding.roundToPx())
- // Icon (when selected) should be `ItemVerticalPadding` from the top
- val selectedIconY = NavigationBarItemVerticalPadding.roundToPx()
+ // Icon (when selected) should be `contentVerticalPadding` from top
+ val selectedIconY = contentVerticalPadding
val unselectedIconY =
if (alwaysShowLabel) selectedIconY else (height - iconPlaceable.height) / 2
@@ -588,6 +590,10 @@
// animationProgress.
val offset = (iconDistance * (1 - animationProgress)).roundToInt()
+ // Label should be fixed padding below icon
+ val labelY = selectedIconY + iconPlaceable.height + IndicatorVerticalPadding.roundToPx() +
+ NavigationBarIndicatorToLabelPadding.roundToPx()
+
val containerWidth = constraints.maxWidth
val labelX = (containerWidth - labelPlaceable.width) / 2
@@ -626,12 +632,13 @@
internal val NavigationBarItemHorizontalPadding: Dp = 8.dp
/*@VisibleForTesting*/
-internal val NavigationBarItemVerticalPadding: Dp = 16.dp
+internal val NavigationBarIndicatorToLabelPadding: Dp = 4.dp
private val IndicatorHorizontalPadding: Dp =
(NavigationBarTokens.ActiveIndicatorWidth - NavigationBarTokens.IconSize) / 2
-private val IndicatorVerticalPadding: Dp =
+/*@VisibleForTesting*/
+internal val IndicatorVerticalPadding: Dp =
(NavigationBarTokens.ActiveIndicatorHeight - NavigationBarTokens.IconSize) / 2
private val IndicatorVerticalOffset: Dp = 12.dp
\ No newline at end of file
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
index 1547cab..69c0d59 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
@@ -16,7 +16,6 @@
package androidx.compose.material3
-import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.TweenSpec
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.MutatePriority
@@ -27,6 +26,7 @@
import androidx.compose.foundation.gestures.DraggableState
import androidx.compose.foundation.gestures.GestureCancellationException
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.PressGestureScope
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.gestures.draggable
@@ -52,7 +52,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.MutableState
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
@@ -71,6 +70,7 @@
import androidx.compose.ui.graphics.PointMode
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.compositeOver
+import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.input.pointer.AwaitPointerEventScope
import androidx.compose.ui.input.pointer.PointerId
import androidx.compose.ui.input.pointer.PointerInputChange
@@ -143,6 +143,7 @@
* for this slider. You can create and pass in your own `remember`ed instance to observe
* [Interaction]s and customize the appearance / behavior of this slider in different states.
*/
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Slider(
value: Float,
@@ -156,17 +157,15 @@
colors: SliderColors = SliderDefaults.colors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) {
- require(steps >= 0) { "steps should be >= 0" }
-
- SliderImpl(
+ Slider(
+ value = value,
+ onValueChange = onValueChange,
modifier = modifier,
enabled = enabled,
- interactionSource = interactionSource,
- onValueChange = onValueChange,
onValueChangeFinished = onValueChangeFinished,
+ colors = colors,
+ interactionSource = interactionSource,
steps = steps,
- value = value,
- valueRange = valueRange,
thumb = {
SliderDefaults.Thumb(
interactionSource = interactionSource,
@@ -174,13 +173,14 @@
enabled = enabled
)
},
- track = { sliderPositions ->
+ track = { sliderState ->
SliderDefaults.Track(
colors = colors,
enabled = enabled,
- sliderPositions = sliderPositions
+ sliderState = sliderState
)
- }
+ },
+ valueRange = valueRange
)
}
@@ -219,8 +219,6 @@
* @param enabled controls the enabled state of this slider. When `false`, this component will not
* respond to user input, and it will appear visually disabled and disabled to accessibility
* services.
- * @param valueRange range of values that this slider can take. The passed [value] will be coerced
- * to this range.
* @param onValueChangeFinished called when value change has ended. This should not be used to
* update the slider value (use [onValueChange] instead), but rather to know when the user has
* completed selecting a new value by ending a drag or a click.
@@ -229,15 +227,15 @@
* @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
* for this slider. You can create and pass in your own `remember`ed instance to observe
* [Interaction]s and customize the appearance / behavior of this slider in different states.
- * @param thumb the thumb to be displayed on the slider, it is placed on top of the track. The lambda
- * receives a [SliderPositions] which is used to obtain the current active track and the tick positions
- * if the slider is discrete.
- * @param track the track to be displayed on the slider, it is placed underneath the thumb. The lambda
- * receives a [SliderPositions] which is used to obtain the current active track and the tick positions
- * if the slider is discrete.
* @param steps if greater than 0, specifies the amount of discrete allowable values, evenly
* distributed across the whole value range. If 0, the slider will behave continuously and allow any
* value from the range specified. Must not be negative.
+ * @param thumb the thumb to be displayed on the slider, it is placed on top of the track. The
+ * lambda receives a [SliderState] which is used to obtain the current active track.
+ * @param track the track to be displayed on the slider, it is placed underneath the thumb. The
+ * lambda receives a [SliderState] which is used to obtain the current active track.
+ * @param valueRange range of values that this slider can take. The passed [value] will be coerced
+ * to this range.
*/
@Composable
@ExperimentalMaterial3Api
@@ -246,37 +244,125 @@
onValueChange: (Float) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
- valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
onValueChangeFinished: (() -> Unit)? = null,
colors: SliderColors = SliderDefaults.colors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
- thumb: @Composable (SliderPositions) -> Unit = {
+ /*@IntRange(from = 0)*/
+ steps: Int = 0,
+ thumb: @Composable (SliderState) -> Unit = {
SliderDefaults.Thumb(
interactionSource = interactionSource,
colors = colors,
enabled = enabled
)
},
- track: @Composable (SliderPositions) -> Unit = { sliderPositions ->
+ track: @Composable (SliderState) -> Unit = { sliderState ->
SliderDefaults.Track(
colors = colors,
enabled = enabled,
- sliderPositions = sliderPositions
+ sliderState = sliderState
)
},
- /*@IntRange(from = 0)*/
- steps: Int = 0,
+ valueRange: ClosedFloatingPointRange<Float> = 0f..1f
) {
- require(steps >= 0) { "steps should be >= 0" }
+ val state = remember(
+ steps,
+ valueRange
+ ) {
+ SliderState(
+ value,
+ onValueChange,
+ steps,
+ valueRange,
+ onValueChangeFinished
+ )
+ }
+ state.value = value
+ state.onValueChange = onValueChange
+ state.onValueChangeFinished = onValueChangeFinished
- SliderImpl(
- value = value,
- onValueChange = onValueChange,
+ Slider(
+ state = state,
modifier = modifier,
enabled = enabled,
- valueRange = valueRange,
- steps = steps,
- onValueChangeFinished = onValueChangeFinished,
+ interactionSource = interactionSource,
+ thumb = thumb,
+ track = track
+ )
+}
+
+/**
+ * <a href="https://m3.material.io/components/sliders/overview" class="external" target="_blank">Material Design slider</a>.
+ *
+ * Sliders allow users to make selections from a range of values.
+ *
+ * Sliders reflect a range of values along a bar, from which users may select a single value.
+ * They are ideal for adjusting settings such as volume, brightness, or applying image filters.
+ *
+ * 
+ *
+ * Use continuous sliders to allow users to make meaningful selections that don’t
+ * require a specific value:
+ *
+ * @sample androidx.compose.material3.samples.SliderSample
+ *
+ * You can allow the user to choose only between predefined set of values by specifying the amount
+ * of steps between min and max values:
+ *
+ * @sample androidx.compose.material3.samples.StepsSliderSample
+ *
+ * Slider using a custom thumb:
+ *
+ * @sample androidx.compose.material3.samples.SliderWithCustomThumbSample
+ *
+ * Slider using custom track and thumb:
+ *
+ * @sample androidx.compose.material3.samples.SliderWithCustomTrackAndThumb
+ *
+ * @param state [SliderState] which contains the slider's current value.
+ * @param modifier the [Modifier] to be applied to this slider
+ * @param enabled controls the enabled state of this slider. When `false`, this component will not
+ * respond to user input, and it will appear visually disabled and disabled to accessibility
+ * services.
+ * @param colors [SliderColors] that will be used to resolve the colors used for this slider in
+ * different states. See [SliderDefaults.colors].
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this slider. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this slider in different states.
+ * @param thumb the thumb to be displayed on the slider, it is placed on top of the track. The
+ * lambda receives a [SliderState] which is used to obtain the current active track.
+ * @param track the track to be displayed on the slider, it is placed underneath the thumb. The
+ * lambda receives a [SliderState] which is used to obtain the current active track.
+ */
+@Composable
+@ExperimentalMaterial3Api
+fun Slider(
+ state: SliderState,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ colors: SliderColors = SliderDefaults.colors(),
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+ thumb: @Composable (SliderState) -> Unit = {
+ SliderDefaults.Thumb(
+ interactionSource = interactionSource,
+ colors = colors,
+ enabled = enabled
+ )
+ },
+ track: @Composable (SliderState) -> Unit = { sliderState ->
+ SliderDefaults.Track(
+ colors = colors,
+ enabled = enabled,
+ sliderState = sliderState
+ )
+ }
+) {
+ require(state.steps >= 0) { "steps should be >= 0" }
+
+ SliderImpl(
+ state = state,
+ modifier = modifier,
+ enabled = enabled,
interactionSource = interactionSource,
thumb = thumb,
track = track
@@ -478,93 +564,40 @@
)
}
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SliderImpl(
modifier: Modifier,
+ state: SliderState,
enabled: Boolean,
interactionSource: MutableInteractionSource,
- onValueChange: (Float) -> Unit,
- onValueChangeFinished: (() -> Unit)?,
- steps: Int,
- value: Float,
- valueRange: ClosedFloatingPointRange<Float>,
- thumb: @Composable (SliderPositions) -> Unit,
- track: @Composable (SliderPositions) -> Unit
+ thumb: @Composable (SliderState) -> Unit,
+ track: @Composable (SliderState) -> Unit
) {
- val onValueChangeState = rememberUpdatedState<(Float) -> Unit> {
- if (it != value) {
- onValueChange(it)
- }
- }
-
- val tickFractions = remember(steps) {
- stepsToTickFractions(steps)
- }
-
- val thumbWidth = remember { mutableStateOf(ThumbWidth.value) }
- val totalWidth = remember { mutableStateOf(0) }
-
- fun scaleToUserValue(minPx: Float, maxPx: Float, offset: Float) =
- scale(minPx, maxPx, offset, valueRange.start, valueRange.endInclusive)
-
- fun scaleToOffset(minPx: Float, maxPx: Float, userValue: Float) =
- scale(valueRange.start, valueRange.endInclusive, userValue, minPx, maxPx)
-
- val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
- val rawOffset = remember { mutableStateOf(scaleToOffset(0f, 0f, value)) }
- val pressOffset = remember { mutableStateOf(0f) }
- val coerced = value.coerceIn(valueRange.start, valueRange.endInclusive)
-
- val positionFraction = calcFraction(valueRange.start, valueRange.endInclusive, coerced)
- val sliderPositions = remember {
- SliderPositions(0f..positionFraction, tickFractions)
- }
- sliderPositions.activeRange = 0f..positionFraction
- sliderPositions.tickFractions = tickFractions
-
- val draggableState = remember(valueRange) {
- SliderDraggableState {
- val maxPx = max(totalWidth.value - thumbWidth.value / 2, 0f)
- val minPx = min(thumbWidth.value / 2, maxPx)
- rawOffset.value = (rawOffset.value + it + pressOffset.value)
- pressOffset.value = 0f
- val offsetInTrack = snapValueToTick(rawOffset.value, tickFractions, minPx, maxPx)
- onValueChangeState.value.invoke(scaleToUserValue(minPx, maxPx, offsetInTrack))
- }
- }
-
- val gestureEndAction = rememberUpdatedState {
- if (!draggableState.isDragging) {
- // check isDragging in case the change is still in progress (touch -> drag case)
- onValueChangeFinished?.invoke()
- }
- }
-
+ state.isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
val press = Modifier.sliderTapModifier(
- draggableState,
+ state,
interactionSource,
- totalWidth.value,
- isRtl,
- rawOffset,
- gestureEndAction,
- pressOffset,
enabled
)
-
val drag = Modifier.draggable(
orientation = Orientation.Horizontal,
- reverseDirection = isRtl,
+ reverseDirection = state.isRtl,
enabled = enabled,
interactionSource = interactionSource,
- onDragStopped = { _ -> gestureEndAction.value.invoke() },
- startDragImmediately = draggableState.isDragging,
- state = draggableState
+ onDragStopped = { state.gestureEndAction() },
+ startDragImmediately = state.draggableState.isDragging,
+ state = state.draggableState
)
Layout(
{
- Box(modifier = Modifier.layoutId(SliderComponents.THUMB)) { thumb(sliderPositions) }
- Box(modifier = Modifier.layoutId(SliderComponents.TRACK)) { track(sliderPositions) }
+ Box(modifier = Modifier.layoutId(SliderComponents.THUMB)) {
+ thumb(state)
+ }
+ Box(modifier = Modifier.layoutId(SliderComponents.TRACK)) {
+ track(state)
+ }
},
modifier = modifier
.minimumInteractiveComponentSize()
@@ -573,12 +606,8 @@
minHeight = SliderTokens.HandleHeight
)
.sliderSemantics(
- value,
- enabled,
- onValueChange,
- onValueChangeFinished,
- valueRange,
- steps
+ state,
+ enabled
)
.focusable(enabled, interactionSource)
.then(press)
@@ -600,11 +629,13 @@
val sliderWidth = thumbPlaceable.width + trackPlaceable.width
val sliderHeight = max(trackPlaceable.height, thumbPlaceable.height)
- thumbWidth.value = thumbPlaceable.width.toFloat()
- totalWidth.value = sliderWidth
+ state.updateDimensions(
+ thumbPlaceable.width.toFloat(),
+ sliderWidth
+ )
val trackOffsetX = thumbPlaceable.width / 2
- val thumbOffsetX = ((trackPlaceable.width) * positionFraction).roundToInt()
+ val thumbOffsetX = ((trackPlaceable.width) * state.coercedValueAsFraction).roundToInt()
val trackOffsetY = (sliderHeight - trackPlaceable.height) / 2
val thumbOffsetY = (sliderHeight - thumbPlaceable.height) / 2
@@ -1015,9 +1046,10 @@
val activeTrackColor = colors.trackColor(enabled, active = true)
val inactiveTickColor = colors.tickColor(enabled, active = false)
val activeTickColor = colors.tickColor(enabled, active = true)
- Canvas(modifier
- .fillMaxWidth()
- .height(TrackHeight)
+ Canvas(
+ modifier
+ .fillMaxWidth()
+ .height(TrackHeight)
) {
val isRtl = layoutDirection == LayoutDirection.Rtl
val sliderLeft = Offset(0f, center.y)
@@ -1068,6 +1100,105 @@
}
}
}
+
+ /**
+ * The Default track for [Slider]
+ *
+ * @param sliderState [SliderState] which is used to obtain the current active track.
+ * @param modifier the [Modifier] to be applied to the track.
+ * @param colors [SliderColors] that will be used to resolve the colors used for this track in
+ * different states. See [SliderDefaults.colors].
+ * @param enabled controls the enabled state of this slider. When `false`, this component will
+ * not respond to user input, and it will appear visually disabled and disabled to
+ * accessibility services.
+ */
+ @Composable
+ @ExperimentalMaterial3Api
+ fun Track(
+ sliderState: SliderState,
+ modifier: Modifier = Modifier,
+ colors: SliderColors = colors(),
+ enabled: Boolean = true
+ ) {
+
+ val inactiveTrackColor by colors.trackColor(enabled, active = false)
+ val activeTrackColor by colors.trackColor(enabled, active = true)
+ val inactiveTickColor by colors.tickColor(enabled, active = false)
+ val activeTickColor by colors.tickColor(enabled, active = true)
+ Canvas(
+ modifier
+ .fillMaxWidth()
+ .height(TrackHeight)
+ ) {
+ drawTrack(
+ sliderState.tickFractions,
+ 0f,
+ sliderState.coercedValueAsFraction,
+ inactiveTrackColor,
+ activeTrackColor,
+ inactiveTickColor,
+ activeTickColor
+ )
+ }
+ }
+
+ private fun DrawScope.drawTrack(
+ tickFractions: FloatArray,
+ activeRangeStart: Float,
+ activeRangeEnd: Float,
+ inactiveTrackColor: Color,
+ activeTrackColor: Color,
+ inactiveTickColor: Color,
+ activeTickColor: Color
+ ) {
+ val isRtl = layoutDirection == LayoutDirection.Rtl
+ val sliderLeft = Offset(0f, center.y)
+ val sliderRight = Offset(size.width, center.y)
+ val sliderStart = if (isRtl) sliderRight else sliderLeft
+ val sliderEnd = if (isRtl) sliderLeft else sliderRight
+ val tickSize = TickSize.toPx()
+ val trackStrokeWidth = TrackHeight.toPx()
+ drawLine(
+ inactiveTrackColor,
+ sliderStart,
+ sliderEnd,
+ trackStrokeWidth,
+ StrokeCap.Round
+ )
+ val sliderValueEnd = Offset(
+ sliderStart.x +
+ (sliderEnd.x - sliderStart.x) * activeRangeEnd,
+ center.y
+ )
+
+ val sliderValueStart = Offset(
+ sliderStart.x +
+ (sliderEnd.x - sliderStart.x) * activeRangeStart,
+ center.y
+ )
+
+ drawLine(
+ activeTrackColor,
+ sliderValueStart,
+ sliderValueEnd,
+ trackStrokeWidth,
+ StrokeCap.Round
+ )
+ tickFractions.groupBy {
+ it > activeRangeEnd ||
+ it < activeRangeStart
+ }.forEach { (outsideFraction, list) ->
+ drawPoints(
+ list.map {
+ Offset(lerp(sliderStart, sliderEnd, it).x, center.y)
+ },
+ PointMode.Points,
+ (if (outsideFraction) inactiveTickColor else activeTickColor),
+ tickSize,
+ StrokeCap.Round
+ )
+ }
+ }
}
private fun snapValueToTick(
@@ -1159,37 +1290,72 @@
}.progressSemantics(value, valueRange, steps)
}
+@OptIn(ExperimentalMaterial3Api::class)
+private fun Modifier.sliderSemantics(
+ state: SliderState,
+ enabled: Boolean
+): Modifier {
+ val coerced = state.value.coerceIn(state.valueRange.start, state.valueRange.endInclusive)
+ return semantics {
+ if (!enabled) disabled()
+ setProgress(
+ action = { targetValue ->
+ var newValue = targetValue.coerceIn(
+ state.valueRange.start,
+ state.valueRange.endInclusive
+ )
+ val originalVal = newValue
+ val resolvedValue = if (state.steps > 0) {
+ var distance: Float = newValue
+ for (i in 0..state.steps + 1) {
+ val stepValue = lerp(
+ state.valueRange.start,
+ state.valueRange.endInclusive,
+ i.toFloat() / (state.steps + 1)
+ )
+ if (abs(stepValue - originalVal) <= distance) {
+ distance = abs(stepValue - originalVal)
+ newValue = stepValue
+ }
+ }
+ newValue
+ } else {
+ newValue
+ }
+
+ // This is to keep it consistent with AbsSeekbar.java: return false if no
+ // change from current.
+ if (resolvedValue == coerced) {
+ false
+ } else {
+ state.onValueChange(resolvedValue)
+ state.onValueChangeFinished?.invoke()
+ true
+ }
+ }
+ )
+ }.progressSemantics(state.value, state.valueRange, state.steps)
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
private fun Modifier.sliderTapModifier(
- draggableState: DraggableState,
+ state: SliderState,
interactionSource: MutableInteractionSource,
- maxPx: Int,
- isRtl: Boolean,
- rawOffset: State<Float>,
- gestureEndAction: State<() -> Unit>,
- pressOffset: MutableState<Float>,
enabled: Boolean
) = composed(
factory = {
if (enabled) {
val scope = rememberCoroutineScope()
- pointerInput(draggableState, interactionSource, maxPx, isRtl) {
+ pointerInput(state.draggableState, interactionSource, state.totalWidth, state.isRtl) {
detectTapGestures(
- onPress = { pos ->
- val to = if (isRtl) maxPx - pos.x else pos.x
- pressOffset.value = to - rawOffset.value
- try {
- awaitRelease()
- } catch (_: GestureCancellationException) {
- pressOffset.value = 0f
- }
- },
+ onPress = state.press,
onTap = {
scope.launch {
- draggableState.drag(MutatePriority.UserInput) {
+ state.draggableState.drag(MutatePriority.UserInput) {
// just trigger animation, press offset will be applied
dragBy(0f)
}
- gestureEndAction.value.invoke()
+ state.gestureEndAction()
}
}
)
@@ -1200,31 +1366,11 @@
},
inspectorInfo = debugInspectorInfo {
name = "sliderTapModifier"
- properties["draggableState"] = draggableState
+ properties["state"] = state
properties["interactionSource"] = interactionSource
- properties["maxPx"] = maxPx
- properties["isRtl"] = isRtl
- properties["rawOffset"] = rawOffset
- properties["gestureEndAction"] = gestureEndAction
- properties["pressOffset"] = pressOffset
properties["enabled"] = enabled
})
-private suspend fun animateToTarget(
- draggableState: DraggableState,
- current: Float,
- target: Float,
- velocity: Float
-) {
- draggableState.drag {
- var latestValue = current
- Animatable(initialValue = current).animateTo(target, SliderToTickAnimation, velocity) {
- dragBy(this.value - latestValue)
- latestValue = this.value
- }
- }
-}
-
private fun Modifier.rangeSliderPressDragModifier(
startInteractionSource: MutableInteractionSource,
endInteractionSource: MutableInteractionSource,
@@ -1428,7 +1574,7 @@
private val SliderToTickAnimation = TweenSpec<Float>(durationMillis = 100)
-private class SliderDraggableState(
+internal class SliderDraggableState(
val onDelta: (Float) -> Unit
) : DraggableState {
@@ -1505,4 +1651,119 @@
result = 31 * result + tickFractions.contentHashCode()
return result
}
-}
\ No newline at end of file
+}
+
+/**
+ * Class that holds information about [Slider]'s active range.
+ *
+ * @param initialValue [Float] that indicates the initial
+ * position of the thumb. If outside of [valueRange]
+ * provided, value will be coerced to this range.
+ * @param initialOnValueChange callback in which [value] should be updated.
+ * @param steps if greater than 0, specifies the amounts of discrete values, evenly distributed
+ * between across the whole value range. If 0, range slider will behave as a continuous slider and
+ * allow to choose any value from the range specified. Must not be negative.
+ * @param onValueChangeFinished lambda to be invoked when value change has ended. This callback
+ * shouldn't be used to update the range slider values (use [onValueChange] for that),
+ * but rather to know when the user has completed selecting a new value by ending a drag or a click.
+ * @param valueRange range of values that Slider values can take. [value] will be
+ * coerced to this range.
+ */
+@Stable
+@ExperimentalMaterial3Api
+class SliderState(
+ initialValue: Float = 0f,
+ initialOnValueChange: ((Float) -> Unit)? = null,
+ /*@IntRange(from = 0)*/
+ val steps: Int = 0,
+ val valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
+ var onValueChangeFinished: (() -> Unit)? = null
+) {
+ private var valueState by mutableStateOf(initialValue)
+
+ /**
+ * [Float] that indicates the current value that the thumb
+ * currently is in respect to the track.
+ */
+ var value: Float
+ set(newVal) {
+ val coercedValue = newVal.coerceIn(valueRange.start, valueRange.endInclusive)
+ val snappedValue = snapValueToTick(
+ coercedValue,
+ tickFractions,
+ valueRange.start,
+ valueRange.endInclusive
+ )
+ valueState = snappedValue
+ }
+ get() = valueState
+
+ /**
+ * callback in which value should be updated
+ */
+ internal var onValueChange: (Float) -> Unit = {
+ if (it != value) {
+ initialOnValueChange?.invoke(it) ?: defaultOnValueChange(it)
+ }
+ }
+
+ internal val tickFractions = stepsToTickFractions(steps)
+
+ private var thumbWidth by mutableStateOf(ThumbWidth.value)
+ internal var totalWidth by mutableStateOf(0)
+
+ internal var rawOffset by mutableStateOf(scaleToOffset(0f, 0f, value))
+ internal var pressOffset by mutableStateOf(0f)
+
+ internal var isRtl = false
+
+ internal val coercedValueAsFraction
+ get() = calcFraction(
+ valueRange.start,
+ valueRange.endInclusive,
+ value.coerceIn(valueRange.start, valueRange.endInclusive)
+ )
+
+ internal val draggableState =
+ SliderDraggableState {
+ val maxPx = max(totalWidth - thumbWidth / 2, 0f)
+ val minPx = min(thumbWidth / 2, maxPx)
+ rawOffset = (rawOffset + it + pressOffset)
+ pressOffset = 0f
+ val offsetInTrack = snapValueToTick(rawOffset, tickFractions, minPx, maxPx)
+ onValueChange(scaleToUserValue(minPx, maxPx, offsetInTrack))
+ }
+
+ internal val gestureEndAction = {
+ if (!draggableState.isDragging) {
+ // check isDragging in case the change is still in progress (touch -> drag case)
+ onValueChangeFinished?.invoke()
+ }
+ }
+
+ internal val press: suspend PressGestureScope.(Offset) -> Unit = { pos ->
+ val to = if (isRtl) totalWidth - pos.x else pos.x
+ pressOffset = to - rawOffset
+ try {
+ awaitRelease()
+ } catch (_: GestureCancellationException) {
+ pressOffset = 0f
+ }
+ }
+
+ internal fun updateDimensions(
+ newThumbWidth: Float,
+ newTotalWidth: Int
+ ) {
+ thumbWidth = newThumbWidth
+ totalWidth = newTotalWidth
+ }
+
+ private fun defaultOnValueChange(newVal: Float) { value = newVal }
+
+ private fun scaleToUserValue(minPx: Float, maxPx: Float, offset: Float) =
+ scale(minPx, maxPx, offset, valueRange.start, valueRange.endInclusive)
+
+ private fun scaleToOffset(minPx: Float, maxPx: Float, userValue: Float) =
+ scale(valueRange.start, valueRange.endInclusive, userValue, minPx, maxPx)
+}
diff --git a/compose/runtime/OWNERS b/compose/runtime/OWNERS
index 8983dbc..a9c5fa6 100644
--- a/compose/runtime/OWNERS
+++ b/compose/runtime/OWNERS
@@ -2,6 +2,7 @@
jsproch@google.com
chuckj@google.com
lelandr@google.com
+anbailey@google.com
# Per-file for Playground infra
per-file settings.gradle = yboyar@google.com, dustinlam@google.com, rahulrav@google.com
diff --git a/compose/runtime/runtime-saveable/build.gradle b/compose/runtime/runtime-saveable/build.gradle
index b4d8073..d23376d 100644
--- a/compose/runtime/runtime-saveable/build.gradle
+++ b/compose/runtime/runtime-saveable/build.gradle
@@ -15,9 +15,8 @@
*/
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("AndroidXPlugin")
@@ -25,77 +24,54 @@
id("com.android.library")
}
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
-dependencies {
+androidXMultiplatform {
+ android()
+ if (desktopEnabled) desktop()
- if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- /* When updating dependencies, make sure to make the an an analogous update in the
- corresponding block below */
- api project(":compose:runtime:runtime")
- api "androidx.annotation:annotation:1.1.0"
-
- implementation(libs.kotlinStdlib)
-
- testImplementation(libs.junit)
- testImplementation(libs.truth)
- testImplementation(libs.testCore)
- testImplementation(libs.testRules)
-
- androidTestImplementation projectOrArtifact(':compose:ui:ui')
- androidTestImplementation projectOrArtifact(":compose:ui:ui-test-junit4")
- androidTestImplementation projectOrArtifact(":compose:test-utils")
- androidTestImplementation "androidx.fragment:fragment:1.3.0"
- androidTestImplementation projectOrArtifact(":activity:activity-compose")
- androidTestImplementation(libs.testUiautomator)
- androidTestImplementation(libs.testCore)
- androidTestImplementation(libs.testRules)
- androidTestImplementation(libs.testRunner)
- androidTestImplementation(libs.espressoCore)
- androidTestImplementation(libs.junit)
- androidTestImplementation(libs.truth)
- androidTestImplementation(libs.dexmakerMockito)
- androidTestImplementation(libs.mockitoCore)
-
- lintPublish(project(":compose:runtime:runtime-saveable-lint"))
-
- samples(projectOrArtifact(":compose:runtime:runtime-saveable:runtime-saveable-samples"))
- }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- androidXComposeMultiplatform {
- android()
- desktop()
- }
-
- kotlin {
- /* When updating dependencies, make sure to make the an an analogous update in the
- corresponding block above */
- sourceSets {
- commonMain.dependencies {
+ sourceSets {
+ commonMain {
+ dependencies {
implementation(libs.kotlinStdlibCommon)
api project(":compose:runtime:runtime")
}
+ }
- androidMain.dependencies {
+ commonTest {
+ dependencies {
+ }
+ }
+
+ jvmMain {
+ dependencies {
+ }
+ }
+
+
+ androidMain {
+ dependsOn(jvmMain)
+ dependencies {
implementation(libs.kotlinStdlib)
api "androidx.annotation:annotation:1.1.0"
}
+ }
- // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
- // need to add Robolectric (which must be kept out of androidAndroidTest), use a top
- // level dependencies block instead:
- // `dependencies { testImplementation(libs.robolectric) }`
- androidTest.dependencies {
- implementation(libs.testRules)
- implementation(libs.testRunner)
- implementation(libs.junit)
- implementation(libs.truth)
+ if (desktopEnabled) {
+ desktopMain {
+ dependsOn(jvmMain)
}
+ }
- androidAndroidTest.dependencies {
+ jvmTest {
+ dependencies {
+ }
+ }
+
+ androidAndroidTest {
+ dependsOn(jvmTest)
+ dependencies {
implementation project(':compose:ui:ui')
implementation project(":compose:ui:ui-test-junit4")
implementation project(":compose:test-utils")
@@ -112,10 +88,32 @@
implementation(libs.mockitoCore)
}
}
+
+ // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+ // need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+ // level dependencies block instead:
+ // `dependencies { testImplementation(libs.robolectric) }`
+ androidTest {
+ dependsOn(jvmTest)
+ dependencies {
+ implementation(libs.testRules)
+ implementation(libs.testRunner)
+ implementation(libs.junit)
+ implementation(libs.truth)
+ }
+ }
+
+ if (desktopEnabled) {
+ desktopTest {
+ dependsOn(jvmTest)
+ }
+ }
}
- dependencies {
- samples(projectOrArtifact(":compose:runtime:runtime-saveable:runtime-saveable-samples"))
- }
+}
+
+dependencies {
+ samples(projectOrArtifact(":compose:runtime:runtime-saveable:runtime-saveable-samples"))
+ lintPublish(project(":compose:runtime:runtime-saveable-lint"))
}
androidx {
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index 3ddd28d..0fee604 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -385,7 +385,7 @@
public final class PrimitiveSnapshotStateKt {
method public static inline operator float getValue(androidx.compose.runtime.FloatState, Object? thisObj, kotlin.reflect.KProperty<?> property);
- method public static androidx.compose.runtime.MutableFloatState mutableStateOf(float value);
+ method public static androidx.compose.runtime.MutableFloatState mutableFloatStateOf(float value);
method public static inline operator void setValue(androidx.compose.runtime.MutableFloatState, Object? thisObj, kotlin.reflect.KProperty<?> property, float value);
}
@@ -483,19 +483,19 @@
public final class SnapshotDoubleStateKt {
method public static inline operator double getValue(androidx.compose.runtime.DoubleState, Object? thisObj, kotlin.reflect.KProperty<?> property);
- method public static androidx.compose.runtime.MutableDoubleState mutableStateOf(double value);
+ method public static androidx.compose.runtime.MutableDoubleState mutableDoubleStateOf(double value);
method public static inline operator void setValue(androidx.compose.runtime.MutableDoubleState, Object? thisObj, kotlin.reflect.KProperty<?> property, double value);
}
public final class SnapshotIntStateKt {
method public static inline operator int getValue(androidx.compose.runtime.IntState, Object? thisObj, kotlin.reflect.KProperty<?> property);
- method public static androidx.compose.runtime.MutableIntState mutableStateOf(int value);
+ method public static androidx.compose.runtime.MutableIntState mutableIntStateOf(int value);
method public static inline operator void setValue(androidx.compose.runtime.MutableIntState, Object? thisObj, kotlin.reflect.KProperty<?> property, int value);
}
public final class SnapshotLongStateKt {
method public static inline operator long getValue(androidx.compose.runtime.LongState, Object? thisObj, kotlin.reflect.KProperty<?> property);
- method public static androidx.compose.runtime.MutableLongState mutableStateOf(long value);
+ method public static androidx.compose.runtime.MutableLongState mutableLongStateOf(long value);
method public static inline operator void setValue(androidx.compose.runtime.MutableLongState, Object? thisObj, kotlin.reflect.KProperty<?> property, long value);
}
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index 1bea3e6..1fa82d6 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -433,7 +433,7 @@
public final class PrimitiveSnapshotStateKt {
method public static inline operator float getValue(androidx.compose.runtime.FloatState, Object? thisObj, kotlin.reflect.KProperty<?> property);
- method public static androidx.compose.runtime.MutableFloatState mutableStateOf(float value);
+ method public static androidx.compose.runtime.MutableFloatState mutableFloatStateOf(float value);
method public static inline operator void setValue(androidx.compose.runtime.MutableFloatState, Object? thisObj, kotlin.reflect.KProperty<?> property, float value);
}
@@ -532,19 +532,19 @@
public final class SnapshotDoubleStateKt {
method public static inline operator double getValue(androidx.compose.runtime.DoubleState, Object? thisObj, kotlin.reflect.KProperty<?> property);
- method public static androidx.compose.runtime.MutableDoubleState mutableStateOf(double value);
+ method public static androidx.compose.runtime.MutableDoubleState mutableDoubleStateOf(double value);
method public static inline operator void setValue(androidx.compose.runtime.MutableDoubleState, Object? thisObj, kotlin.reflect.KProperty<?> property, double value);
}
public final class SnapshotIntStateKt {
method public static inline operator int getValue(androidx.compose.runtime.IntState, Object? thisObj, kotlin.reflect.KProperty<?> property);
- method public static androidx.compose.runtime.MutableIntState mutableStateOf(int value);
+ method public static androidx.compose.runtime.MutableIntState mutableIntStateOf(int value);
method public static inline operator void setValue(androidx.compose.runtime.MutableIntState, Object? thisObj, kotlin.reflect.KProperty<?> property, int value);
}
public final class SnapshotLongStateKt {
method public static inline operator long getValue(androidx.compose.runtime.LongState, Object? thisObj, kotlin.reflect.KProperty<?> property);
- method public static androidx.compose.runtime.MutableLongState mutableStateOf(long value);
+ method public static androidx.compose.runtime.MutableLongState mutableLongStateOf(long value);
method public static inline operator void setValue(androidx.compose.runtime.MutableLongState, Object? thisObj, kotlin.reflect.KProperty<?> property, long value);
}
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index 55a4ec2..5ce4dbc 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -417,7 +417,7 @@
public final class PrimitiveSnapshotStateKt {
method public static inline operator float getValue(androidx.compose.runtime.FloatState, Object? thisObj, kotlin.reflect.KProperty<?> property);
- method public static androidx.compose.runtime.MutableFloatState mutableStateOf(float value);
+ method public static androidx.compose.runtime.MutableFloatState mutableFloatStateOf(float value);
method public static inline operator void setValue(androidx.compose.runtime.MutableFloatState, Object? thisObj, kotlin.reflect.KProperty<?> property, float value);
}
@@ -519,19 +519,19 @@
public final class SnapshotDoubleStateKt {
method public static inline operator double getValue(androidx.compose.runtime.DoubleState, Object? thisObj, kotlin.reflect.KProperty<?> property);
- method public static androidx.compose.runtime.MutableDoubleState mutableStateOf(double value);
+ method public static androidx.compose.runtime.MutableDoubleState mutableDoubleStateOf(double value);
method public static inline operator void setValue(androidx.compose.runtime.MutableDoubleState, Object? thisObj, kotlin.reflect.KProperty<?> property, double value);
}
public final class SnapshotIntStateKt {
method public static inline operator int getValue(androidx.compose.runtime.IntState, Object? thisObj, kotlin.reflect.KProperty<?> property);
- method public static androidx.compose.runtime.MutableIntState mutableStateOf(int value);
+ method public static androidx.compose.runtime.MutableIntState mutableIntStateOf(int value);
method public static inline operator void setValue(androidx.compose.runtime.MutableIntState, Object? thisObj, kotlin.reflect.KProperty<?> property, int value);
}
public final class SnapshotLongStateKt {
method public static inline operator long getValue(androidx.compose.runtime.LongState, Object? thisObj, kotlin.reflect.KProperty<?> property);
- method public static androidx.compose.runtime.MutableLongState mutableStateOf(long value);
+ method public static androidx.compose.runtime.MutableLongState mutableLongStateOf(long value);
method public static inline operator void setValue(androidx.compose.runtime.MutableLongState, Object? thisObj, kotlin.reflect.KProperty<?> property, long value);
}
diff --git a/compose/runtime/runtime/build.gradle b/compose/runtime/runtime/build.gradle
index f7317d7..37a15fa 100644
--- a/compose/runtime/runtime/build.gradle
+++ b/compose/runtime/runtime/build.gradle
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("AndroidXPlugin")
@@ -24,87 +23,95 @@
id("com.android.library")
}
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
-dependencies {
+androidXMultiplatform {
+ android()
+ if (desktopEnabled) desktop()
- if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- /*
- * When updating dependencies, make sure to make the an an analogous update in the
- * corresponding block below
- */
-
- api(libs.kotlinCoroutinesAndroid)
-
- implementation("androidx.annotation:annotation:1.1.0")
- implementation(libs.kotlinStdlib)
-
- testImplementation(libs.kotlinTestJunit)
- testImplementation(libs.junit)
- testImplementation(libs.robolectric)
- testImplementation(libs.kotlinCoroutinesTest)
-
- androidTestImplementation(libs.kotlinTestJunit)
- androidTestImplementation(libs.testExtJunit)
- androidTestImplementation(libs.testRules)
- androidTestImplementation(libs.testRunner)
- androidTestImplementation(libs.junit)
- androidTestImplementation(libs.truth)
-
- lintChecks(projectOrArtifact(":compose:runtime:runtime-lint"))
- lintPublish(projectOrArtifact(":compose:runtime:runtime-lint"))
-
- samples(projectOrArtifact(":compose:runtime:runtime:runtime-samples"))
- }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- androidXComposeMultiplatform {
- android()
- desktop()
- }
-
- kotlin {
-
- /*
- * When updating dependencies, make sure to make the an an analogous update in the
- * corresponding block above
- */
- sourceSets {
- commonMain.dependencies {
+ sourceSets {
+ commonMain {
+ dependencies {
implementation(libs.kotlinStdlibCommon)
implementation(libs.kotlinCoroutinesCore)
}
- jvmMain.dependencies {
- implementation(libs.kotlinStdlib)
- api(libs.kotlinCoroutinesCore)
- }
- androidMain {
- dependencies {
- api(libs.kotlinCoroutinesAndroid)
- api("androidx.annotation:annotation:1.1.0")
- }
- }
+ }
- commonTest.dependencies {
+ commonTest {
+ dependencies {
implementation kotlin("test")
implementation(libs.kotlinCoroutinesTest)
}
+ }
- androidAndroidTest {
+ jvmMain {
+ dependencies {
+ implementation(libs.kotlinStdlib)
+ api(libs.kotlinCoroutinesCore)
+ }
+ }
+
+
+ androidMain {
+ dependsOn(jvmMain)
+ dependencies {
+ api(libs.kotlinCoroutinesAndroid)
+ api("androidx.annotation:annotation:1.1.0")
+ }
+ }
+
+ if (desktopEnabled) {
+ desktopMain {
+ dependsOn(jvmMain)
+ }
+ }
+
+ jvmTest {
+ dependsOn(commonTest)
+ dependencies {
+ }
+ }
+
+ nonEmulatorCommonTest {
+ dependsOn(commonTest)
+ dependencies {
+ }
+ }
+
+ nonEmulatorJvmTest {
+ dependsOn(nonEmulatorCommonTest)
+ dependencies {
+ }
+ }
+
+ androidAndroidTest {
+ dependsOn(jvmTest)
+ dependencies {
+ implementation(libs.testExtJunit)
+ implementation(libs.testRules)
+ implementation(libs.testRunner)
+ implementation(libs.truth)
+ }
+ }
+
+ androidTest {
+ dependsOn(jvmTest)
+ dependsOn(nonEmulatorJvmTest)
+ }
+
+ if (desktopEnabled) {
+ desktopTest {
dependsOn(jvmTest)
- dependencies {
- implementation(libs.testExtJunit)
- implementation(libs.testRules)
- implementation(libs.testRunner)
- implementation(libs.truth)
- }
+ dependsOn(nonEmulatorJvmTest)
}
}
}
- dependencies {
- samples(projectOrArtifact(":compose:runtime:runtime:runtime-samples"))
- }
+}
+
+dependencies {
+ lintChecks(projectOrArtifact(":compose:runtime:runtime-lint"))
+ lintPublish(projectOrArtifact(":compose:runtime:runtime-lint"))
+ samples(projectOrArtifact(":compose:runtime:runtime:runtime-samples"))
}
android {
diff --git a/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/SnapshotStateAutoboxingBenchmark.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/SnapshotStateAutoboxingBenchmark.kt
index 4710164..6ae06e8 100644
--- a/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/SnapshotStateAutoboxingBenchmark.kt
+++ b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/SnapshotStateAutoboxingBenchmark.kt
@@ -19,6 +19,7 @@
import androidx.benchmark.junit4.measureRepeated
import androidx.compose.runtime.MutableFloatState
import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
@@ -44,7 +45,7 @@
@Before
fun setup() {
- primitiveFloatState = mutableStateOf(-1.0f)
+ primitiveFloatState = mutableFloatStateOf(-1.0f)
autoboxingFloatState = mutableStateOf(-1.0f)
}
diff --git a/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/snapshots/ParcelablePrimitiveMutableStateTests.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/snapshots/ParcelablePrimitiveMutableStateTests.kt
index 65a3781..0dc3606 100644
--- a/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/snapshots/ParcelablePrimitiveMutableStateTests.kt
+++ b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/snapshots/ParcelablePrimitiveMutableStateTests.kt
@@ -18,14 +18,17 @@
import android.os.Parcel
import android.os.Parcelable
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableDoubleStateOf
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableLongStateOf
import kotlin.test.assertEquals
import org.junit.Test
class ParcelablePrimitiveMutableStateTests {
@Test
fun saveAndRestoreMutableIntState() {
- val state = mutableStateOf(0)
+ val state = mutableIntStateOf(0)
state.intValue = 1
val restored = recreateViaParcel(state)
@@ -34,7 +37,7 @@
@Test
fun saveAndRestoreMutableLongState() {
- val state = mutableStateOf(0L)
+ val state = mutableLongStateOf(0L)
state.longValue = 1
val restored = recreateViaParcel(state)
@@ -43,7 +46,7 @@
@Test
fun saveAndRestoreMutableFloatState() {
- val state = mutableStateOf(0f)
+ val state = mutableFloatStateOf(0f)
state.floatValue = 1.5f
val restored = recreateViaParcel(state)
@@ -52,7 +55,7 @@
@Test
fun saveAndRestoreMutableDoubleState() {
- val state = mutableStateOf(0.0)
+ val state = mutableDoubleStateOf(0.0)
state.doubleValue = 1.5
val restored = recreateViaParcel(state)
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt
index 0d74edd..4fa3504 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt
@@ -27,9 +27,8 @@
import androidx.compose.runtime.snapshots.newWritableRecord
import androidx.compose.runtime.snapshots.sync
import androidx.compose.runtime.snapshots.withCurrent
-// Explicit imports for these needed in common source sets.
-import kotlin.jvm.JvmName
import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
import kotlin.math.min
/**
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ProduceState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ProduceState.kt
index 1905768..cd26a4c 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ProduceState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ProduceState.kt
@@ -18,12 +18,11 @@
@file:JvmMultifileClass
package androidx.compose.runtime
+import kotlin.coroutines.CoroutineContext
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.suspendCancellableCoroutine
-import kotlin.coroutines.CoroutineContext
-// Explicit imports for these needed in common source sets.
-import kotlin.jvm.JvmName
-import kotlin.jvm.JvmMultifileClass
/**
* Receiver scope for use with [produceState].
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt
index 71de1aa..f4fb14b 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt
@@ -41,8 +41,12 @@
*
* @see DoubleState
* @see MutableDoubleState
+ * @see mutableStateOf
+ * @see mutableIntStateOf
+ * @see mutableLongStateOf
+ * @see mutableFloatStateOf
*/
-fun mutableStateOf(
+fun mutableDoubleStateOf(
value: Double
): MutableDoubleState = createSnapshotMutableDoubleState(value)
@@ -51,7 +55,7 @@
* function cause the current [RecomposeScope] to subscribe to changes of that value.
*
* @see MutableDoubleState
- * @see mutableStateOf
+ * @see mutableDoubleStateOf
*/
@Stable
@JvmDefaultWithCompatibility
@@ -80,7 +84,7 @@
* scheduled.
*
* @see [DoubleState]
- * @see [mutableStateOf]
+ * @see [mutableDoubleStateOf]
*/
@Stable
@JvmDefaultWithCompatibility
@@ -116,7 +120,7 @@
*
* @param value the wrapped value
*
- * @see [mutableStateOf]
+ * @see [mutableDoubleStateOf]
*/
internal open class SnapshotMutableDoubleStateImpl(
value: Double
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt
index b48a3fb..df4a2f7 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt
@@ -40,8 +40,12 @@
*
* @see FloatState
* @see MutableFloatState
+ * @see mutableStateOf
+ * @see mutableIntStateOf
+ * @see mutableLongStateOf
+ * @see mutableDoubleStateOf
*/
-fun mutableStateOf(
+fun mutableFloatStateOf(
value: Float
): MutableFloatState = createSnapshotMutableFloatState(value)
@@ -50,7 +54,7 @@
* function cause the current [RecomposeScope] to subscribe to changes of that value.
*
* @see MutableFloatState
- * @see mutableStateOf
+ * @see mutableDoubleStateOf
*/
@Stable
@JvmDefaultWithCompatibility
@@ -76,7 +80,7 @@
* scheduled.
*
* @see [FloatState]
- * @see [mutableStateOf]
+ * @see [mutableDoubleStateOf]
*/
@Stable
@JvmDefaultWithCompatibility
@@ -112,7 +116,7 @@
*
* @param value the wrapped value
*
- * @see [mutableStateOf]
+ * @see [mutableDoubleStateOf]
*/
internal open class SnapshotMutableFloatStateImpl(
value: Float
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFlow.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFlow.kt
index 12250ab..3d7c842 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFlow.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFlow.kt
@@ -19,17 +19,16 @@
package androidx.compose.runtime
import androidx.compose.runtime.snapshots.Snapshot
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withContext
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
-// Explicit imports for these needed in common source sets.
-import kotlin.jvm.JvmName
-import kotlin.jvm.JvmMultifileClass
/**
* Collects values from this [StateFlow] and represents its latest value via [State].
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt
index 785f9b7..ec700ee 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt
@@ -40,8 +40,12 @@
*
* @see IntState
* @see MutableIntState
+ * @see mutableStateOf
+ * @see mutableLongStateOf
+ * @see mutableFloatStateOf
+ * @see mutableDoubleStateOf
*/
-fun mutableStateOf(
+fun mutableIntStateOf(
value: Int
): MutableIntState = createSnapshotMutableIntState(value)
@@ -50,7 +54,7 @@
* function cause the current [RecomposeScope] to subscribe to changes of that value.
*
* @see MutableIntState
- * @see mutableStateOf
+ * @see mutableDoubleStateOf
*/
@Stable
@JvmDefaultWithCompatibility
@@ -76,7 +80,7 @@
* scheduled.
*
* @see [IntState]
- * @see [mutableStateOf]
+ * @see [mutableDoubleStateOf]
*/
@Stable
@JvmDefaultWithCompatibility
@@ -116,7 +120,7 @@
*
* @param value the wrapped value
*
- * @see [mutableStateOf]
+ * @see [mutableDoubleStateOf]
*/
internal open class SnapshotMutableIntStateImpl(
value: Int
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt
index e129f42..a95ae2e 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt
@@ -41,8 +41,12 @@
*
* @see LongState
* @see MutableLongState
+ * @see mutableStateOf
+ * @see mutableIntStateOf
+ * @see mutableFloatStateOf
+ * @see mutableDoubleStateOf
*/
-fun mutableStateOf(
+fun mutableLongStateOf(
value: Long
): MutableLongState = createSnapshotMutableLongState(value)
@@ -51,7 +55,7 @@
* function cause the current [RecomposeScope] to subscribe to changes of that value.
*
* @see MutableLongState
- * @see mutableStateOf
+ * @see mutableDoubleStateOf
*/
@Stable
@JvmDefaultWithCompatibility
@@ -77,7 +81,7 @@
* scheduled.
*
* @see [LongState]
- * @see [mutableStateOf]
+ * @see [mutableDoubleStateOf]
*/
@Stable
@JvmDefaultWithCompatibility
@@ -113,7 +117,7 @@
*
* @param value the wrapped value
*
- * @see [mutableStateOf]
+ * @see [mutableDoubleStateOf]
*/
internal open class SnapshotMutableLongStateImpl(
value: Long
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotMutationPolicy.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotMutationPolicy.kt
index 69f2809..5b301a9 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotMutationPolicy.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotMutationPolicy.kt
@@ -18,11 +18,10 @@
@file:JvmMultifileClass
package androidx.compose.runtime
-import androidx.compose.runtime.snapshots.MutableSnapshot
-// Explicit imports for these needed in common source sets.
-import kotlin.jvm.JvmName
-import kotlin.jvm.JvmMultifileClass
import androidx.compose.runtime.internal.JvmDefaultWithCompatibility
+import androidx.compose.runtime.snapshots.MutableSnapshot
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
/**
* A policy to control how the result of [mutableStateOf] report and merge changes to
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
index c506f56..d408eb3 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
@@ -27,9 +27,8 @@
import androidx.compose.runtime.snapshots.overwritable
import androidx.compose.runtime.snapshots.readable
import androidx.compose.runtime.snapshots.withCurrent
-// Explicit imports for jvm annotations needed in common source sets.
-import kotlin.jvm.JvmName
import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
import kotlin.reflect.KProperty
/**
@@ -49,6 +48,10 @@
* @see State
* @see MutableState
* @see SnapshotMutationPolicy
+ * @see mutableIntStateOf
+ * @see mutableLongStateOf
+ * @see mutableFloatStateOf
+ * @see mutableDoubleStateOf
*/
fun <T> mutableStateOf(
value: T,
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/AbstractApplierTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/AbstractApplierTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/AbstractApplierTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/AbstractApplierTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/BroadcastFrameClockTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/BroadcastFrameClockTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/BroadcastFrameClockTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/BroadcastFrameClockTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/EffectsTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/EffectsTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/EffectsTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/EffectsTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/LatchTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/LatchTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/LatchTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/LatchTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/ModelViewTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/ModelViewTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/ModelViewTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/ModelViewTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/RecomposerTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/RestartTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/RestartTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/RestartTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/RestartTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/SlotTableTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/SlotTableTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/SlotTableTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/SlotTableTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/SnapshotFlowTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/SnapshotFlowTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/SnapshotFlowTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/SnapshotFlowTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/UpdateChangedFlagsTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/UpdateChangedFlagsTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/UpdateChangedFlagsTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/UpdateChangedFlagsTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMapTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMapTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMapTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMapTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayMapTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayMapTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayMapTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayMapTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposeContact.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposeContact.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposeContact.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposeContact.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposePoints.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposePoints.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposePoints.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposePoints.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposeReport.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposeReport.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ComposeReport.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ComposeReport.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Contact.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Contact.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Contact.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Contact.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ContactModel.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ContactModel.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ContactModel.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ContactModel.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/EmptyApplier.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/EmptyApplier.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/EmptyApplier.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/EmptyApplier.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Point.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Point.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Point.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Point.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Report.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Report.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Report.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Report.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/View.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/View.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/View.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/View.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ViewApplier.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ViewApplier.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/ViewApplier.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/ViewApplier.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Views.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Views.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Views.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/mock/Views.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/DerivedSnapshotStateTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/DerivedSnapshotStateTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/DerivedSnapshotStateTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/DerivedSnapshotStateTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/PrimitiveSnapshotStateTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/PrimitiveSnapshotStateTests.kt
similarity index 92%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/PrimitiveSnapshotStateTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/PrimitiveSnapshotStateTests.kt
index 1f6833d..9fb0d40 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/PrimitiveSnapshotStateTests.kt
+++ b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/PrimitiveSnapshotStateTests.kt
@@ -20,7 +20,10 @@
import androidx.compose.runtime.MutableFloatState
import androidx.compose.runtime.MutableIntState
import androidx.compose.runtime.MutableLongState
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableDoubleStateOf
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableLongStateOf
import kotlin.reflect.KCallable
import kotlin.reflect.KMutableProperty1
import kotlin.test.assertEquals
@@ -95,7 +98,7 @@
arrayOf(
PrimitiveSnapshotStateImplementation(
clazz = MutableIntState::class.java,
- creator = ::mutableStateOf,
+ creator = ::mutableIntStateOf,
valueProperty = MutableIntState::intValue,
sampleValues = generateSequence(1) { it + 1 }
)
@@ -103,7 +106,7 @@
arrayOf(
PrimitiveSnapshotStateImplementation(
clazz = MutableLongState::class.java,
- creator = ::mutableStateOf,
+ creator = ::mutableLongStateOf,
valueProperty = MutableLongState::longValue,
sampleValues = generateSequence(1) { it + 1 }
)
@@ -111,7 +114,7 @@
arrayOf(
PrimitiveSnapshotStateImplementation(
clazz = MutableFloatState::class.java,
- creator = ::mutableStateOf,
+ creator = ::mutableFloatStateOf,
valueProperty = MutableFloatState::floatValue,
sampleValues = generateSequence(1f) { it + 1 }
)
@@ -119,7 +122,7 @@
arrayOf(
PrimitiveSnapshotStateImplementation(
clazz = MutableDoubleState::class.java,
- creator = ::mutableStateOf,
+ creator = ::mutableDoubleStateOf,
valueProperty = MutableDoubleState::value,
sampleValues = generateSequence(1.0) { it + 1 }
)
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElementTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElementTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElementTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElementTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotDoubleIndexHeapTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotDoubleIndexHeapTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotDoubleIndexHeapTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotDoubleIndexHeapTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateExtensionsTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateExtensionsTests.kt
similarity index 95%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateExtensionsTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateExtensionsTests.kt
index 3bf8a3c..d2b2cf3 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateExtensionsTests.kt
+++ b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateExtensionsTests.kt
@@ -20,7 +20,10 @@
import androidx.compose.runtime.asFloatState
import androidx.compose.runtime.asIntState
import androidx.compose.runtime.asLongState
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableDoubleStateOf
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableLongStateOf
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.test.assertEquals
@@ -44,7 +47,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
fun stateAsIntStateDispatchesSnapshotUpdates() = runTest {
val snapshotObservationJob = Job()
- val state = mutableStateOf(512)
+ val state = mutableIntStateOf(512)
val intState = state.asIntState()
val intSnapshotHistory = getSnapshotHistory(snapshotObservationJob) { intState.intValue }
advanceGlobalSnapshotAndSettle()
@@ -82,7 +85,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
fun stateAsLongStateDispatchesSnapshotUpdates() = runTest {
val snapshotObservationJob = Job()
- val state = mutableStateOf(1000L)
+ val state = mutableLongStateOf(1000L)
val longState = state.asLongState()
val longSnapshotHistory = getSnapshotHistory(snapshotObservationJob) { longState.longValue }
advanceGlobalSnapshotAndSettle()
@@ -120,7 +123,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
fun stateAsFloatStateDispatchesSnapshotUpdates() = runTest {
val snapshotObservationJob = Job()
- val state = mutableStateOf(0.0f)
+ val state = mutableFloatStateOf(0.0f)
val floatState = state.asFloatState()
val floatSnapshotHistory = getSnapshotHistory(snapshotObservationJob) {
floatState.floatValue
@@ -160,7 +163,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
fun stateAsDoubleStateDispatchesSnapshotUpdates() = runTest {
val snapshotObservationJob = Job()
- val state = mutableStateOf(1.0)
+ val state = mutableDoubleStateOf(1.0)
val doubleState = state.asDoubleState()
val doubleSnapshotHistory = getSnapshotHistory(snapshotObservationJob) {
doubleState.doubleValue
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsCommon.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsCommon.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsCommon.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsCommon.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSetTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSetTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSetTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSetTests.kt
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt
rename to compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/JvmCompositionTests.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/JvmCompositionTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/JvmCompositionTests.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/JvmCompositionTests.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/LiveEditTests.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/LiveEditTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/LiveEditTests.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/LiveEditTests.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/MonotonicFrameClockTest.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/MonotonicFrameClockTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/MonotonicFrameClockTest.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/MonotonicFrameClockTest.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/RecomposerTests.jvm.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/RecomposerTests.jvm.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/RecomposerTests.jvm.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/RecomposerTests.jvm.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/reflect/ComposableMethodTest.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/reflect/ComposableMethodTest.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/reflect/ComposableMethodTest.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/reflect/ComposableMethodTest.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsJvm.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsJvm.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsJvm.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTestsJvm.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTestsJvm.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTestsJvm.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTestsJvm.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTestsJvm.kt
diff --git a/compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotThreadMapTests.kt b/compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotThreadMapTests.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotThreadMapTests.kt
rename to compose/runtime/runtime/src/nonEmulatorJvmTest/kotlin/androidx/compose/runtime/snapshots/SnapshotThreadMapTests.kt
diff --git a/compose/test-utils/build.gradle b/compose/test-utils/build.gradle
index 5b5d26c..46eecc3 100644
--- a/compose/test-utils/build.gradle
+++ b/compose/test-utils/build.gradle
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-import androidx.build.AndroidXComposePlugin
import androidx.build.LibraryType
import androidx.build.Publish
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("AndroidXPlugin")
@@ -25,89 +23,93 @@
id("AndroidXComposePlugin")
}
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = false // b/276387374 TODO: KmpPlatformsKt.enableDesktop(project)
-dependencies {
+androidXMultiplatform {
+ android()
+ if (desktopEnabled) desktop()
- if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- /*
- * When updating dependencies, make sure to make the an an analogous update in the
- * corresponding block below
- */
-
- api("androidx.activity:activity:1.2.0")
- api(projectOrArtifact(":compose:ui:ui-test-junit4"))
- api(project(":test:screenshot:screenshot"))
-
- implementation(libs.kotlinStdlibCommon)
- implementation(projectOrArtifact(":compose:runtime:runtime"))
- implementation(projectOrArtifact(":compose:ui:ui-unit"))
- implementation(projectOrArtifact(":compose:ui:ui-graphics"))
- implementation("androidx.activity:activity-compose:1.3.1")
- // old version of common-java8 conflicts with newer version, because both have
- // DefaultLifecycleEventObserver.
- // Outside of androidx this is resolved via constraint added to lifecycle-common,
- // but it doesn't work in androidx.
- // See aosp/1804059
- implementation("androidx.lifecycle:lifecycle-common-java8:2.5.1")
- implementation(libs.testCore)
- implementation(libs.testRules)
-
- // This has stub APIs for access to legacy Android APIs, so we don't want
- // any dependency on this module.
- compileOnly(projectOrArtifact(":compose:ui:ui-android-stubs"))
-
- testImplementation(libs.truth)
-
- androidTestImplementation(libs.truth)
- androidTestImplementation(projectOrArtifact(":compose:material:material"))
- }
-}
-
-if (AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- androidXComposeMultiplatform {
- android()
- }
-
- kotlin {
- /*
- * When updating dependencies, make sure to make the an an analogous update in the
- * corresponding block above
- */
- sourceSets {
- commonMain.dependencies {
+ sourceSets {
+ commonMain {
+ dependencies {
implementation(libs.kotlinStdlibCommon)
implementation(projectOrArtifact(":compose:runtime:runtime"))
implementation(projectOrArtifact(":compose:ui:ui-unit"))
implementation(projectOrArtifact(":compose:ui:ui-graphics"))
implementation(projectOrArtifact(":compose:ui:ui-test-junit4"))
}
+ }
+ androidMain.dependencies {
+ api("androidx.activity:activity:1.2.0")
+ implementation "androidx.activity:activity-compose:1.3.1"
+ api(projectOrArtifact(":compose:ui:ui-test-junit4"))
+ api(project(":test:screenshot:screenshot"))
+ // This has stub APIs for access to legacy Android APIs, so we don't want
+ // any dependency on this module.
+ compileOnly(projectOrArtifact(":compose:ui:ui-android-stubs"))
+ implementation(libs.testCore)
+ implementation(libs.testRules)
+ }
- androidMain.dependencies {
- api("androidx.activity:activity:1.2.0")
- implementation "androidx.activity:activity-compose:1.3.1"
- api(projectOrArtifact(":compose:ui:ui-test-junit4"))
- api(project(":test:screenshot:screenshot"))
- // This has stub APIs for access to legacy Android APIs, so we don't want
- // any dependency on this module.
- compileOnly(projectOrArtifact(":compose:ui:ui-android-stubs"))
- implementation(libs.testCore)
- implementation(libs.testRules)
+ commonTest {
+ dependencies {
}
+ }
- // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
- // need to add Robolectric (which must be kept out of androidAndroidTest), use a top
- // level dependencies block instead:
- // `dependencies { testImplementation(libs.robolectric) }`
- androidTest.dependencies {
- implementation(libs.truth)
+ jvmMain {
+ dependsOn(commonMain)
+ dependencies {
}
+ }
- androidAndroidTest.dependencies {
+
+ androidMain {
+ dependsOn(jvmMain)
+ dependencies {
+ }
+ }
+
+ if (desktopEnabled) {
+ desktopMain {
+ dependsOn(jvmMain)
+ dependencies {
+ }
+ }
+ }
+
+ jvmTest {
+ dependsOn(commonTest)
+ dependencies {
+ }
+ }
+
+ androidAndroidTest {
+ dependsOn(jvmTest)
+ dependencies {
implementation(libs.truth)
implementation(projectOrArtifact(":compose:material:material"))
}
}
+
+ // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+ // need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+ // level dependencies block instead:
+ // `dependencies { testImplementation(libs.robolectric) }`
+ androidTest {
+ dependsOn(jvmTest)
+ dependencies {
+ implementation(libs.truth)
+ }
+ }
+
+ if (desktopEnabled) {
+ desktopTest {
+ dependsOn(jvmTest)
+ dependsOn(desktopMain)
+ dependencies {
+ }
+ }
+ }
}
}
diff --git a/compose/ui/ui-geometry/build.gradle b/compose/ui/ui-geometry/build.gradle
index 1a05daf..15e6e16 100644
--- a/compose/ui/ui-geometry/build.gradle
+++ b/compose/ui/ui-geometry/build.gradle
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("AndroidXPlugin")
@@ -24,53 +23,69 @@
id("AndroidXComposePlugin")
}
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
-if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- dependencies {
- /*
- * When updating dependencies, make sure to make the an an analogous update in the
- * corresponding block below
- */
+androidXMultiplatform {
+ android()
+ if (desktopEnabled) desktop()
- api("androidx.annotation:annotation:1.1.0")
-
- implementation("androidx.compose.runtime:runtime:1.2.1")
- implementation(project(":compose:ui:ui-util"))
- implementation(libs.kotlinStdlib)
-
- testImplementation(libs.junit)
- testImplementation(libs.truth)
- testImplementation(libs.kotlinTest)
- }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- androidXComposeMultiplatform {
- android()
- desktop()
- }
-
- kotlin {
- /*
- * When updating dependencies, make sure to make the an an analogous update in the
- * corresponding block above
- */
- sourceSets {
- commonMain.dependencies {
+ sourceSets {
+ commonMain {
+ dependencies {
implementation(libs.kotlinStdlibCommon)
- implementation(project(":compose:runtime:runtime"))
+ implementation("androidx.compose.runtime:runtime:1.2.1")
implementation(project(":compose:ui:ui-util"))
}
- jvmMain.dependencies {
+ }
+
+ commonTest {
+ dependencies {
+ implementation(kotlin("test-junit"))
+ }
+ }
+
+ jvmMain {
+ dependencies {
implementation(libs.kotlinStdlib)
}
- androidMain.dependencies {
+ }
+
+
+ androidMain {
+ dependsOn(jvmMain)
+ dependencies {
api("androidx.annotation:annotation:1.1.0")
}
- commonTest.dependencies {
- implementation(kotlin("test-junit"))
+ }
+
+ if (desktopEnabled) {
+ desktopMain {
+ dependsOn(jvmMain)
+ dependencies {
+ implementation(project(":compose:runtime:runtime"))
+ }
+ }
+ }
+
+ jvmTest {
+ dependencies {
+ }
+ }
+
+ androidAndroidTest {
+ dependsOn(jvmTest)
+ dependencies {
+ }
+ }
+
+ androidTest {
+ dependsOn(jvmTest)
+ }
+
+ if (desktopEnabled) {
+ desktopTest {
+ dependsOn(jvmTest)
}
}
}
diff --git a/compose/ui/ui-graphics/api/current.txt b/compose/ui/ui-graphics/api/current.txt
index 7b32178..ae86521 100644
--- a/compose/ui/ui-graphics/api/current.txt
+++ b/compose/ui/ui-graphics/api/current.txt
@@ -15,6 +15,11 @@
method public static androidx.compose.ui.graphics.ColorFilter asComposeColorFilter(android.graphics.ColorFilter);
}
+ public final class AndroidColorSpace_androidKt {
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.ColorSpace toAndroidColorSpace(androidx.compose.ui.graphics.colorspace.ColorSpace);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.colorspace.ColorSpace toComposeColorSpace(android.graphics.ColorSpace);
+ }
+
public final class AndroidImageBitmap_androidKt {
method public static android.graphics.Bitmap asAndroidBitmap(androidx.compose.ui.graphics.ImageBitmap);
method public static androidx.compose.ui.graphics.ImageBitmap asImageBitmap(android.graphics.Bitmap);
@@ -395,6 +400,10 @@
property public final float[] values;
}
+ public fun interface ColorProducer {
+ method public long invoke();
+ }
+
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class FilterQuality {
method public int getValue();
property public final int value;
@@ -625,6 +634,7 @@
method public void reset();
method public default void rewind();
method public void setFillType(int);
+ method public default void transform(float[] matrix);
method public void translate(long offset);
property public abstract int fillType;
property public abstract boolean isConvex;
@@ -1611,5 +1621,9 @@
method public androidx.compose.ui.graphics.Path toPath(optional androidx.compose.ui.graphics.Path target);
}
+ public final class PathParserKt {
+ method public static androidx.compose.ui.graphics.Path toPath(java.util.List<? extends androidx.compose.ui.graphics.vector.PathNode>, optional androidx.compose.ui.graphics.Path target);
+ }
+
}
diff --git a/compose/ui/ui-graphics/api/public_plus_experimental_current.txt b/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
index f7a4628..d6fde28 100644
--- a/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
@@ -15,6 +15,11 @@
method public static androidx.compose.ui.graphics.ColorFilter asComposeColorFilter(android.graphics.ColorFilter);
}
+ public final class AndroidColorSpace_androidKt {
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.ColorSpace toAndroidColorSpace(androidx.compose.ui.graphics.colorspace.ColorSpace);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.colorspace.ColorSpace toComposeColorSpace(android.graphics.ColorSpace);
+ }
+
public final class AndroidImageBitmap_androidKt {
method public static android.graphics.Bitmap asAndroidBitmap(androidx.compose.ui.graphics.ImageBitmap);
method public static androidx.compose.ui.graphics.ImageBitmap asImageBitmap(android.graphics.Bitmap);
@@ -395,6 +400,10 @@
property public final float[] values;
}
+ public fun interface ColorProducer {
+ method public long invoke();
+ }
+
@kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalGraphicsApi {
}
@@ -628,6 +637,7 @@
method public void reset();
method public default void rewind();
method public void setFillType(int);
+ method public default void transform(float[] matrix);
method public void translate(long offset);
property public abstract int fillType;
property public abstract boolean isConvex;
@@ -1614,5 +1624,9 @@
method public androidx.compose.ui.graphics.Path toPath(optional androidx.compose.ui.graphics.Path target);
}
+ public final class PathParserKt {
+ method public static androidx.compose.ui.graphics.Path toPath(java.util.List<? extends androidx.compose.ui.graphics.vector.PathNode>, optional androidx.compose.ui.graphics.Path target);
+ }
+
}
diff --git a/compose/ui/ui-graphics/api/restricted_current.txt b/compose/ui/ui-graphics/api/restricted_current.txt
index 172d06a..17269b9 100644
--- a/compose/ui/ui-graphics/api/restricted_current.txt
+++ b/compose/ui/ui-graphics/api/restricted_current.txt
@@ -45,6 +45,11 @@
method public static androidx.compose.ui.graphics.ColorFilter asComposeColorFilter(android.graphics.ColorFilter);
}
+ public final class AndroidColorSpace_androidKt {
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.ColorSpace toAndroidColorSpace(androidx.compose.ui.graphics.colorspace.ColorSpace);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.colorspace.ColorSpace toComposeColorSpace(android.graphics.ColorSpace);
+ }
+
public final class AndroidImageBitmap_androidKt {
method public static android.graphics.Bitmap asAndroidBitmap(androidx.compose.ui.graphics.ImageBitmap);
method public static androidx.compose.ui.graphics.ImageBitmap asImageBitmap(android.graphics.Bitmap);
@@ -426,6 +431,10 @@
property public final float[] values;
}
+ public fun interface ColorProducer {
+ method public long invoke();
+ }
+
public final class DegreesKt {
method @kotlin.PublishedApi internal static float degrees(float radians);
}
@@ -660,6 +669,7 @@
method public void reset();
method public default void rewind();
method public void setFillType(int);
+ method public default void transform(float[] matrix);
method public void translate(long offset);
property public abstract int fillType;
property public abstract boolean isConvex;
@@ -1670,5 +1680,9 @@
method public androidx.compose.ui.graphics.Path toPath(optional androidx.compose.ui.graphics.Path target);
}
+ public final class PathParserKt {
+ method public static androidx.compose.ui.graphics.Path toPath(java.util.List<? extends androidx.compose.ui.graphics.vector.PathNode>, optional androidx.compose.ui.graphics.Path target);
+ }
+
}
diff --git a/compose/ui/ui-graphics/samples/src/main/java/androidx/compose/ui/graphics/samples/BrushSamples.kt b/compose/ui/ui-graphics/samples/src/main/java/androidx/compose/ui/graphics/samples/BrushSamples.kt
index 4784e83..b825b33 100644
--- a/compose/ui/ui-graphics/samples/src/main/java/androidx/compose/ui/graphics/samples/BrushSamples.kt
+++ b/compose/ui/ui-graphics/samples/src/main/java/androidx/compose/ui/graphics/samples/BrushSamples.kt
@@ -26,8 +26,10 @@
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.unit.dp
@Sampled
@@ -57,4 +59,108 @@
val sweep = Brush.sweepGradient(listOf(Color.Cyan, Color.Magenta))
Box(modifier = Modifier.size(120.dp).background(sweep))
}
+}
+
+@Sampled
+fun LinearGradientColorStopSample() {
+ Brush.linearGradient(
+ 0.0f to Color.Red,
+ 0.3f to Color.Green,
+ 1.0f to Color.Blue,
+ start = Offset(0.0f, 50.0f),
+ end = Offset(0.0f, 100.0f)
+ )
+}
+
+@Sampled
+fun LinearGradientSample() {
+ Brush.linearGradient(
+ listOf(Color.Red, Color.Green, Color.Blue),
+ start = Offset(0.0f, 50.0f),
+ end = Offset(0.0f, 100.0f)
+ )
+}
+
+@Sampled
+fun HorizontalGradientSample() {
+ Brush.horizontalGradient(
+ listOf(Color.Red, Color.Green, Color.Blue),
+ startX = 10.0f,
+ endX = 20.0f
+ )
+}
+
+@Sampled
+fun HorizontalGradientColorStopSample() {
+ Brush.horizontalGradient(
+ 0.0f to Color.Red,
+ 0.3f to Color.Green,
+ 1.0f to Color.Blue,
+ startX = 0.0f,
+ endX = 100.0f
+ )
+}
+
+@Sampled
+fun VerticalGradientColorStopSample() {
+ Brush.verticalGradient(
+ 0.1f to Color.Red,
+ 0.3f to Color.Green,
+ 0.5f to Color.Blue,
+ startY = 0.0f,
+ endY = 100.0f
+ )
+}
+
+@Sampled
+fun VerticalGradientSample() {
+ Brush.verticalGradient(
+ listOf(Color.Red, Color.Green, Color.Blue),
+ startY = 0.0f,
+ endY = 100.0f
+ )
+}
+
+@Sampled
+fun RadialBrushColorStopSample() {
+ val side1 = 100
+ val side2 = 200
+ Brush.radialGradient(
+ 0.0f to Color.Red,
+ 0.3f to Color.Green,
+ 1.0f to Color.Blue,
+ center = Offset(side1 / 2.0f, side2 / 2.0f),
+ radius = side1 / 2.0f,
+ tileMode = TileMode.Repeated
+ )
+}
+
+@Sampled
+fun RadialBrushSample() {
+ val side1 = 100
+ val side2 = 200
+ Brush.radialGradient(
+ listOf(Color.Red, Color.Green, Color.Blue),
+ center = Offset(side1 / 2.0f, side2 / 2.0f),
+ radius = side1 / 2.0f,
+ tileMode = TileMode.Repeated
+ )
+}
+
+@Sampled
+fun SweepGradientColorStopSample() {
+ Brush.sweepGradient(
+ 0.0f to Color.Red,
+ 0.3f to Color.Green,
+ 1.0f to Color.Blue,
+ center = Offset(0.0f, 100.0f)
+ )
+}
+
+@Sampled
+fun SweepGradientSample() {
+ Brush.sweepGradient(
+ listOf(Color.Red, Color.Green, Color.Blue),
+ center = Offset(10.0f, 20.0f)
+ )
}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidColorSpaceTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidColorSpaceTest.kt
new file mode 100644
index 0000000..ca5301a
--- /dev/null
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidColorSpaceTest.kt
@@ -0,0 +1,290 @@
+/*
+ * 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.compose.ui.graphics
+
+import android.graphics.ColorSpace
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.ui.graphics.AndroidColorSpaceTest.ColorSpaceHelper.Companion.colorSpaceTestHelper
+import androidx.compose.ui.graphics.colorspace.ColorSpaces
+import androidx.compose.ui.graphics.colorspace.Rgb
+import androidx.compose.ui.graphics.colorspace.TransferParameters
+import androidx.compose.ui.graphics.colorspace.WhitePoint
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AndroidColorSpaceTest {
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testSrgbColorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.Srgb, // Compose
+ ColorSpace.get(ColorSpace.Named.SRGB) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testAcesColorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.Aces, // Compose
+ ColorSpace.get(ColorSpace.Named.ACES) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testAcescgColorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.Acescg, // Compose
+ ColorSpace.get(ColorSpace.Named.ACESCG) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testAdobeRgbColorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.AdobeRgb, // Compose
+ ColorSpace.get(ColorSpace.Named.ADOBE_RGB) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testBt2020Colorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.Bt2020, // Compose
+ ColorSpace.get(ColorSpace.Named.BT2020) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testBt709Colorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.Bt709, // Compose
+ ColorSpace.get(ColorSpace.Named.BT709) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testCieLabColorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.CieLab, // Compose
+ ColorSpace.get(ColorSpace.Named.CIE_LAB) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testCieXyzColorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.CieXyz, // Compose
+ ColorSpace.get(ColorSpace.Named.CIE_XYZ) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testDciP3Colorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.DciP3, // Compose
+ ColorSpace.get(ColorSpace.Named.DCI_P3) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testDisplayP3Colorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.DisplayP3, // Compose
+ ColorSpace.get(ColorSpace.Named.DISPLAY_P3) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testExtendedSrgbColorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.ExtendedSrgb, // Compose
+ ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testLinearExtendedSrgbColorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.LinearExtendedSrgb, // Compose
+ ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testLinearSrgbColorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.LinearSrgb, // Compose
+ ColorSpace.get(ColorSpace.Named.LINEAR_SRGB) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testNtsc1953Colorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.Ntsc1953, // Compose
+ ColorSpace.get(ColorSpace.Named.NTSC_1953) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testProPhotoRgbColorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.ProPhotoRgb, // Compose
+ ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testSmpteCColorspace() {
+ colorSpaceTestHelper(
+ ColorSpaces.SmpteC, // Compose
+ ColorSpace.get(ColorSpace.Named.SMPTE_C) // Framework
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testUnknownColorspace3WhitePointValues() {
+ val name = "MyCustomColorSpace"
+ val whitePoint = floatArrayOf(1.0f, 2.0f, 3.0f)
+ val transferParameters = ColorSpace.Rgb.TransferParameters(
+ 0.1, // a
+ 0.2, // b
+ 0.3, // c
+ 0.4, // d
+ 0.5, // e
+ 0.6, // f
+ 0.7 // g
+ )
+ val primaries = floatArrayOf(1f, 2f, 3f, 4f, 5f, 6f)
+ colorSpaceTestHelper(
+ androidx.compose.ui.graphics.colorspace.Rgb(
+ name = name,
+ primaries = primaries,
+ WhitePoint(1.0f, 2.0f, 3.0f),
+ TransferParameters(0.7, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6)
+ ),
+ ColorSpace.Rgb(
+ name,
+ primaries,
+ whitePoint,
+ transferParameters
+ )
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testUnknownColorSpaceNoTransform() {
+ val name = "MyCustomColorSpace"
+ val whitePoint = floatArrayOf(1.0f, 2.0f, 3.0f)
+ val primaries = floatArrayOf(1f, 2f, 3f, 4f, 5f, 6f)
+ colorSpaceTestHelper(
+ androidx.compose.ui.graphics.colorspace.Rgb(
+ name = name,
+ primaries = primaries,
+ WhitePoint(1.0f, 2.0f, 3.0f),
+ { 1.0 },
+ { 2.0 },
+ 2f,
+ 4f,
+ ),
+ ColorSpace.Rgb(
+ name,
+ primaries,
+ whitePoint,
+ { _ -> 1.0 },
+ { _ -> 2.0 },
+ 2f,
+ 4f,
+ )
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testUnknownColorspace2WhitePointValues() {
+ val name = "MyCustomColorSpace"
+ val whitePoint = floatArrayOf(1.0f, 2.0f)
+ val transferParameters = ColorSpace.Rgb.TransferParameters(
+ 0.1, // a
+ 0.2, // b
+ 0.3, // c
+ 0.4, // d
+ 0.5, // e
+ 0.6, // f
+ 0.7 // g
+ )
+ val primaries = floatArrayOf(1f, 2f, 3f, 4f, 5f, 6f)
+
+ colorSpaceTestHelper(
+ Rgb(
+ name = name,
+ primaries = primaries,
+ WhitePoint(1.0f, 2.0f),
+ TransferParameters(0.7, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6)
+ ),
+ ColorSpace.Rgb(
+ name,
+ primaries,
+ whitePoint,
+ transferParameters
+ )
+ )
+ }
+
+ // Helper class to avoid NoSuchClassExceptions being thrown when tests are run on an older
+ // API level that does not understand ColorSpace APIs
+ internal class ColorSpaceHelper {
+ companion object {
+ @RequiresApi(Build.VERSION_CODES.O)
+ fun colorSpaceTestHelper(
+ composeColorSpace: androidx.compose.ui.graphics.colorspace.ColorSpace,
+ frameworkColorSpace: ColorSpace
+ ) {
+ val convertedColorSpace = frameworkColorSpace.toComposeColorSpace()
+ Assert.assertEquals(composeColorSpace, convertedColorSpace)
+
+ val frameworkConvertedColorSpace = convertedColorSpace.toAndroidColorSpace()
+ Assert.assertEquals(frameworkColorSpace, frameworkConvertedColorSpace)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ImageBitmapTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ImageBitmapTest.kt
index e081e68..ab73407 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ImageBitmapTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ImageBitmapTest.kt
@@ -16,22 +16,13 @@
package androidx.compose.ui.graphics
-import android.graphics.ColorSpace
-import android.graphics.ColorSpace.Named
-import android.graphics.ColorSpace.Rgb
-import android.os.Build
-import androidx.annotation.RequiresApi
-import androidx.compose.ui.graphics.ImageBitmapTest.ColorSpaceHelper.Companion.colorSpaceTestHelper
import androidx.compose.ui.graphics.colorspace.ColorSpaces
-import androidx.compose.ui.graphics.colorspace.TransferParameters
-import androidx.compose.ui.graphics.colorspace.WhitePoint
import androidx.test.filters.SmallTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Test
import org.junit.runner.RunWith
import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SdkSuppress
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -54,227 +45,4 @@
assertFalse(image.hasAlpha)
assertEquals(cs, image.colorSpace)
}
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testSrgbColorspace() {
- colorSpaceTestHelper(
- ColorSpaces.Srgb, // Compose
- ColorSpace.get(Named.SRGB) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testAcesColorspace() {
- colorSpaceTestHelper(
- ColorSpaces.Aces, // Compose
- ColorSpace.get(Named.ACES) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testAcescgColorspace() {
- colorSpaceTestHelper(
- ColorSpaces.Acescg, // Compose
- ColorSpace.get(Named.ACESCG) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testAdobeRgbColorspace() {
- colorSpaceTestHelper(
- ColorSpaces.AdobeRgb, // Compose
- ColorSpace.get(Named.ADOBE_RGB) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testBt2020Colorspace() {
- colorSpaceTestHelper(
- ColorSpaces.Bt2020, // Compose
- ColorSpace.get(Named.BT2020) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testBt709Colorspace() {
- colorSpaceTestHelper(
- ColorSpaces.Bt709, // Compose
- ColorSpace.get(Named.BT709) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testCieLabColorspace() {
- colorSpaceTestHelper(
- ColorSpaces.CieLab, // Compose
- ColorSpace.get(Named.CIE_LAB) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testCieXyzColorspace() {
- colorSpaceTestHelper(
- ColorSpaces.CieXyz, // Compose
- ColorSpace.get(Named.CIE_XYZ) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testDciP3Colorspace() {
- colorSpaceTestHelper(
- ColorSpaces.DciP3, // Compose
- ColorSpace.get(Named.DCI_P3) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testDisplayP3Colorspace() {
- colorSpaceTestHelper(
- ColorSpaces.DisplayP3, // Compose
- ColorSpace.get(Named.DISPLAY_P3) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testExtendedSrgbColorspace() {
- colorSpaceTestHelper(
- ColorSpaces.ExtendedSrgb, // Compose
- ColorSpace.get(Named.EXTENDED_SRGB) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testLinearExtendedSrgbColorspace() {
- colorSpaceTestHelper(
- ColorSpaces.LinearExtendedSrgb, // Compose
- ColorSpace.get(Named.LINEAR_EXTENDED_SRGB) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testLinearSrgbColorspace() {
- colorSpaceTestHelper(
- ColorSpaces.LinearSrgb, // Compose
- ColorSpace.get(Named.LINEAR_SRGB) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testNtsc1953Colorspace() {
- colorSpaceTestHelper(
- ColorSpaces.Ntsc1953, // Compose
- ColorSpace.get(Named.NTSC_1953) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testProPhotoRgbColorspace() {
- colorSpaceTestHelper(
- ColorSpaces.ProPhotoRgb, // Compose
- ColorSpace.get(Named.PRO_PHOTO_RGB) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testSmpteCColorspace() {
- colorSpaceTestHelper(
- ColorSpaces.SmpteC, // Compose
- ColorSpace.get(Named.SMPTE_C) // Framework
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testUnknownColorspace3WhitePointValues() {
- val name = "MyCustomColorSpace"
- val whitePoint = floatArrayOf(1.0f, 2.0f, 3.0f)
- val transferParameters = Rgb.TransferParameters(
- 0.1, // a
- 0.2, // b
- 0.3, // c
- 0.4, // d
- 0.5, // e
- 0.6, // f
- 0.7 // g
- )
- val primaries = floatArrayOf(1f, 2f, 3f, 4f, 5f, 6f)
- colorSpaceTestHelper(
- androidx.compose.ui.graphics.colorspace.Rgb(
- name = name,
- primaries = primaries,
- WhitePoint(1.0f, 2.0f, 3.0f),
- TransferParameters(0.7, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6)
- ),
- Rgb(
- name,
- primaries,
- whitePoint,
- transferParameters
- )
- )
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun testUnknownColorspace2WhitePointValues() {
- val name = "MyCustomColorSpace"
- val whitePoint = floatArrayOf(1.0f, 2.0f)
- val transferParameters = Rgb.TransferParameters(
- 0.1, // a
- 0.2, // b
- 0.3, // c
- 0.4, // d
- 0.5, // e
- 0.6, // f
- 0.7 // g
- )
- val primaries = floatArrayOf(1f, 2f, 3f, 4f, 5f, 6f)
-
- colorSpaceTestHelper(
- androidx.compose.ui.graphics.colorspace.Rgb(
- name = name,
- primaries = primaries,
- WhitePoint(1.0f, 2.0f),
- TransferParameters(0.7, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6)
- ),
- Rgb(
- name,
- primaries,
- whitePoint,
- transferParameters
- )
- )
- }
-
- // Helper class to avoid NoSuchClassExceptions being thrown when tests are run on an older
- // API level that does not understand ColorSpace APIs
- internal class ColorSpaceHelper {
- companion object {
- @RequiresApi(Build.VERSION_CODES.O)
- fun colorSpaceTestHelper(
- composeColorSpace: androidx.compose.ui.graphics.colorspace.ColorSpace,
- frameworkColorSpace: ColorSpace
- ) {
- with(Api26Bitmap) {
- assertEquals(composeColorSpace, frameworkColorSpace.composeColorSpace())
- }
- }
- }
- }
}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathTest.kt
index 6975ef7..def70c3 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathTest.kt
@@ -97,6 +97,37 @@
assertEquals(0, androidPath.resetCount)
}
+ @Test
+ fun testPathTransform() {
+ val width = 100
+ val height = 100
+ val image = ImageBitmap(width, height)
+ val canvas = Canvas(image)
+
+ val path = Path().apply {
+ addRect(Rect(0f, 0f, 50f, 50f))
+ transform(
+ Matrix().apply { translate(50f, 50f) }
+ )
+ }
+
+ val paint = Paint().apply { color = Color.Black }
+ canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
+ paint.color = Color.Red
+ canvas.drawPath(path, paint)
+
+ image.toPixelMap().apply {
+ assertEquals(Color.Black, this[width / 2 - 3, height / 2 - 3])
+ assertEquals(Color.Black, this[width / 2, height / 2 - 3])
+ assertEquals(Color.Black, this[width / 2 - 3, height / 2])
+
+ assertEquals(Color.Red, this[width / 2 + 2, height / 2 + 2])
+ assertEquals(Color.Red, this[width - 2, height / 2 + 2])
+ assertEquals(Color.Red, this[width - 2, height - 2])
+ assertEquals(Color.Red, this[width / 2 + 2, height - 2])
+ }
+ }
+
class TestAndroidPath : android.graphics.Path() {
var resetCount = 0
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ShaderTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ShaderTest.kt
index 786ed1f..a1c9071 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ShaderTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ShaderTest.kt
@@ -28,6 +28,8 @@
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.math.roundToInt
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -197,6 +199,53 @@
)
}
+ @Test
+ fun testInvalidWidthBrush() {
+ // Verify that attempts to create a RadialGradient with a width of 0 do not throw
+ // IllegalArgumentExceptions for an invalid radius
+ val brush = Brush.radialGradient(listOf(Color.Red, Color.Blue))
+ val paint = Paint()
+ brush.applyTo(Size(0f, 10f), paint, 1.0f)
+ }
+
+ @Test
+ fun testInvalidHeightBrush() {
+ val brush = Brush.radialGradient(listOf(Color.Red, Color.Blue))
+ val paint = Paint()
+ // Verify that attempts to create a RadialGradient with a height of 0 do not throw
+ // IllegalArgumentExceptions for an invalid radius
+ brush.applyTo(Size(10f, 0f), paint, 1.0f)
+ }
+
+ @Test
+ fun testValidToInvalidWidthBrush() {
+ // Verify that attempts to create a RadialGradient with a non-zero width/height that
+ // is later attempted to be recreated with a zero width remove the shader from the Paint
+ val brush = Brush.radialGradient(listOf(Color.Red, Color.Blue))
+ val paint = Paint()
+ brush.applyTo(Size(10f, 10f), paint, 1.0f)
+
+ assertNotNull(paint.shader)
+
+ brush.applyTo(Size(0f, 10f), paint, 1.0f)
+ assertNull(paint.shader)
+ }
+
+ @Test
+ fun testValidToInvalidHeightBrush() {
+ // Verify that attempts to create a RadialGradient with a non-zero width/height that
+ // is later attempted to be recreated with a zero height remove the shader from the Paint
+ val brush = Brush.radialGradient(listOf(Color.Red, Color.Blue))
+ val paint = Paint()
+
+ brush.applyTo(Size(10f, 10f), paint, 1.0f)
+
+ assertNotNull(paint.shader)
+
+ brush.applyTo(Size(10f, 0f), paint, 1.0f)
+ assertNull(paint.shader)
+ }
+
private fun ImageBitmap.drawInto(
block: DrawScope.() -> Unit
) = CanvasDrawScope().draw(
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidCanvas.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidCanvas.android.kt
index 1293ae9..8f2debb 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidCanvas.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidCanvas.android.kt
@@ -335,7 +335,9 @@
*/
private fun drawLines(points: List<Offset>, paint: Paint, stepBy: Int) {
if (points.size >= 2) {
- for (i in 0 until points.size - 1 step stepBy) {
+ val frameworkPaint = paint.asFrameworkPaint()
+ var i = 0
+ while (i < points.size - 1) {
val p1 = points[i]
val p2 = points[i + 1]
internalCanvas.drawLine(
@@ -343,8 +345,9 @@
p1.y,
p2.x,
p2.y,
- paint.asFrameworkPaint()
+ frameworkPaint
)
+ i += stepBy
}
}
}
@@ -365,10 +368,13 @@
private fun drawRawPoints(points: FloatArray, paint: Paint, stepBy: Int) {
if (points.size % 2 == 0) {
- for (i in 0 until points.size - 1 step stepBy) {
+ val frameworkPaint = paint.asFrameworkPaint()
+ var i = 0
+ while (i < points.size - 1) {
val x = points[i]
val y = points[i + 1]
- internalCanvas.drawPoint(x, y, paint.asFrameworkPaint())
+ internalCanvas.drawPoint(x, y, frameworkPaint)
+ i += stepBy
}
}
}
@@ -390,18 +396,15 @@
// Float array is treated as alternative set of x and y coordinates
// x1, y1, x2, y2, x3, y3, ... etc.
if (points.size >= 4 && points.size % 2 == 0) {
- for (i in 0 until points.size - 3 step stepBy * 2) {
+ val frameworkPaint = paint.asFrameworkPaint()
+ var i = 0
+ while (i < points.size - 3) {
val x1 = points[i]
val y1 = points[i + 1]
val x2 = points[i + 2]
val y2 = points[i + 3]
- internalCanvas.drawLine(
- x1,
- y1,
- x2,
- y2,
- paint.asFrameworkPaint()
- )
+ internalCanvas.drawLine(x1, y1, x2, y2, frameworkPaint)
+ i += stepBy * 2
}
}
}
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorSpace.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorSpace.android.kt
new file mode 100644
index 0000000..37086be
--- /dev/null
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorSpace.android.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2020 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.graphics
+
+import android.graphics.ColorSpace.get
+import android.os.Build
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+import androidx.compose.ui.graphics.colorspace.ColorSpace
+import androidx.compose.ui.graphics.colorspace.ColorSpaces
+import androidx.compose.ui.graphics.colorspace.Rgb
+import androidx.compose.ui.graphics.colorspace.TransferParameters
+import androidx.compose.ui.graphics.colorspace.WhitePoint
+
+/**
+ * Convert the Compose [ColorSpace] into an Android framework [android.graphics.ColorSpace]
+ */
+@RequiresApi(Build.VERSION_CODES.O)
+fun ColorSpace.toAndroidColorSpace(): android.graphics.ColorSpace =
+ with(ColorSpaceVerificationHelper) {
+ androidColorSpace()
+ }
+
+/**
+ * Convert the [android.graphics.ColorSpace] into a Compose [ColorSpace]
+ */
+@RequiresApi(Build.VERSION_CODES.O)
+fun android.graphics.ColorSpace.toComposeColorSpace() =
+ with(ColorSpaceVerificationHelper) {
+ composeColorSpace()
+ }
+
+@RequiresApi(Build.VERSION_CODES.O)
+private object ColorSpaceVerificationHelper {
+
+ @DoNotInline
+ @JvmStatic
+ @RequiresApi(Build.VERSION_CODES.O)
+ fun ColorSpace.androidColorSpace(): android.graphics.ColorSpace {
+ return when (this) {
+ ColorSpaces.Srgb -> get(android.graphics.ColorSpace.Named.SRGB)
+ ColorSpaces.Aces -> get(android.graphics.ColorSpace.Named.ACES)
+ ColorSpaces.Acescg -> get(android.graphics.ColorSpace.Named.ACESCG)
+ ColorSpaces.AdobeRgb -> get(android.graphics.ColorSpace.Named.ADOBE_RGB)
+ ColorSpaces.Bt2020 -> get(android.graphics.ColorSpace.Named.BT2020)
+ ColorSpaces.Bt709 -> get(android.graphics.ColorSpace.Named.BT709)
+ ColorSpaces.CieLab -> get(android.graphics.ColorSpace.Named.CIE_LAB)
+ ColorSpaces.CieXyz -> get(android.graphics.ColorSpace.Named.CIE_XYZ)
+ ColorSpaces.DciP3 -> get(android.graphics.ColorSpace.Named.DCI_P3)
+ ColorSpaces.DisplayP3 -> get(android.graphics.ColorSpace.Named.DISPLAY_P3)
+ ColorSpaces.ExtendedSrgb -> get(android.graphics.ColorSpace.Named.EXTENDED_SRGB)
+ ColorSpaces.LinearExtendedSrgb ->
+ get(android.graphics.ColorSpace.Named.LINEAR_EXTENDED_SRGB)
+ ColorSpaces.LinearSrgb -> get(android.graphics.ColorSpace.Named.LINEAR_SRGB)
+ ColorSpaces.Ntsc1953 -> get(android.graphics.ColorSpace.Named.NTSC_1953)
+ ColorSpaces.ProPhotoRgb -> get(android.graphics.ColorSpace.Named.PRO_PHOTO_RGB)
+ ColorSpaces.SmpteC -> get(android.graphics.ColorSpace.Named.SMPTE_C)
+ else -> {
+ if (this is Rgb) {
+ val whitePointArray = this.whitePoint.toXyz()
+ val transferParams = this.transferParameters
+ val androidTransferParams = if (transferParams != null) {
+ android.graphics.ColorSpace.Rgb.TransferParameters(
+ transferParams.a,
+ transferParams.b,
+ transferParams.c,
+ transferParams.d,
+ transferParams.e,
+ transferParams.f,
+ transferParams.gamma
+ )
+ } else {
+ null
+ }
+ if (androidTransferParams != null) {
+ android.graphics.ColorSpace.Rgb(
+ this.name,
+ this.primaries,
+ whitePointArray,
+ androidTransferParams
+ )
+ } else {
+ android.graphics.ColorSpace.Rgb(
+ this.name,
+ this.primaries,
+ whitePointArray,
+ this.oetf,
+ this.eotf,
+ this.getMinValue(0),
+ this.getMaxValue(0)
+ )
+ }
+ } else {
+ get(android.graphics.ColorSpace.Named.SRGB)
+ }
+ }
+ }
+ }
+
+ @DoNotInline
+ @JvmStatic
+ @RequiresApi(Build.VERSION_CODES.O)
+ fun android.graphics.ColorSpace.composeColorSpace(): ColorSpace {
+ return when (this.id) {
+ android.graphics.ColorSpace.Named.SRGB.ordinal ->
+ ColorSpaces.Srgb
+ android.graphics.ColorSpace.Named.ACES.ordinal ->
+ ColorSpaces.Aces
+ android.graphics.ColorSpace.Named.ACESCG.ordinal ->
+ ColorSpaces.Acescg
+ android.graphics.ColorSpace.Named.ADOBE_RGB.ordinal ->
+ ColorSpaces.AdobeRgb
+ android.graphics.ColorSpace.Named.BT2020.ordinal ->
+ ColorSpaces.Bt2020
+ android.graphics.ColorSpace.Named.BT709.ordinal ->
+ ColorSpaces.Bt709
+ android.graphics.ColorSpace.Named.CIE_LAB.ordinal ->
+ ColorSpaces.CieLab
+ android.graphics.ColorSpace.Named.CIE_XYZ.ordinal ->
+ ColorSpaces.CieXyz
+ android.graphics.ColorSpace.Named.DCI_P3.ordinal ->
+ ColorSpaces.DciP3
+ android.graphics.ColorSpace.Named.DISPLAY_P3.ordinal ->
+ ColorSpaces.DisplayP3
+ android.graphics.ColorSpace.Named.EXTENDED_SRGB.ordinal ->
+ ColorSpaces.ExtendedSrgb
+ android.graphics.ColorSpace.Named.LINEAR_EXTENDED_SRGB.ordinal ->
+ ColorSpaces.LinearExtendedSrgb
+ android.graphics.ColorSpace.Named.LINEAR_SRGB.ordinal ->
+ ColorSpaces.LinearSrgb
+ android.graphics.ColorSpace.Named.NTSC_1953.ordinal ->
+ ColorSpaces.Ntsc1953
+ android.graphics.ColorSpace.Named.PRO_PHOTO_RGB.ordinal ->
+ ColorSpaces.ProPhotoRgb
+ android.graphics.ColorSpace.Named.SMPTE_C.ordinal ->
+ ColorSpaces.SmpteC
+ else -> {
+ if (this is android.graphics.ColorSpace.Rgb) {
+ val transferParams = this.transferParameters
+ val whitePoint = if (this.whitePoint.size == 3) {
+ WhitePoint(this.whitePoint[0], this.whitePoint[1], this.whitePoint[2])
+ } else {
+ WhitePoint(this.whitePoint[0], this.whitePoint[1])
+ }
+
+ val composeTransferParams = if (transferParams != null) {
+ TransferParameters(
+ gamma = transferParams.g,
+ a = transferParams.a,
+ b = transferParams.b,
+ c = transferParams.c,
+ d = transferParams.d,
+ e = transferParams.e,
+ f = transferParams.f
+ )
+ } else {
+ null
+ }
+ Rgb(
+ name = this.name,
+ primaries = this.primaries,
+ whitePoint = whitePoint,
+ transform = this.transform,
+ oetf = { x -> this.oetf.applyAsDouble(x) },
+ eotf = { x -> this.eotf.applyAsDouble(x) },
+ min = this.getMinValue(0),
+ max = this.getMaxValue(0),
+ transferParameters = composeTransferParams,
+ id = this.id
+ )
+ } else {
+ ColorSpaces.Srgb
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.android.kt
index 2e7700c..93d2f9c 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.android.kt
@@ -17,16 +17,12 @@
package androidx.compose.ui.graphics
import android.graphics.Bitmap
-import android.graphics.ColorSpace.Named
import android.os.Build
import android.util.DisplayMetrics
import androidx.annotation.DoNotInline
import androidx.annotation.RequiresApi
import androidx.compose.ui.graphics.colorspace.ColorSpace
import androidx.compose.ui.graphics.colorspace.ColorSpaces
-import androidx.compose.ui.graphics.colorspace.Rgb
-import androidx.compose.ui.graphics.colorspace.TransferParameters
-import androidx.compose.ui.graphics.colorspace.WhitePoint
/**
* Create an [ImageBitmap] from the given [Bitmap]. Note this does
@@ -205,99 +201,12 @@
height,
bitmapConfig.toBitmapConfig(),
hasAlpha,
- colorSpace.toFrameworkColorSpace()
+ colorSpace.toAndroidColorSpace()
)
}
@DoNotInline
@JvmStatic
internal fun Bitmap.composeColorSpace() =
- colorSpace?.composeColorSpace() ?: ColorSpaces.Srgb
-
- @DoNotInline
- @JvmStatic
- internal fun ColorSpace.toFrameworkColorSpace(): android.graphics.ColorSpace {
- val frameworkNamedSpace = when (this) {
- ColorSpaces.Srgb -> Named.SRGB
- ColorSpaces.Aces -> Named.ACES
- ColorSpaces.Acescg -> Named.ACESCG
- ColorSpaces.AdobeRgb -> Named.ADOBE_RGB
- ColorSpaces.Bt2020 -> Named.BT2020
- ColorSpaces.Bt709 -> Named.BT709
- ColorSpaces.CieLab -> Named.CIE_LAB
- ColorSpaces.CieXyz -> Named.CIE_XYZ
- ColorSpaces.DciP3 -> Named.DCI_P3
- ColorSpaces.DisplayP3 -> Named.DISPLAY_P3
- ColorSpaces.ExtendedSrgb -> Named.EXTENDED_SRGB
- ColorSpaces.LinearExtendedSrgb ->
- Named.LINEAR_EXTENDED_SRGB
- ColorSpaces.LinearSrgb -> Named.LINEAR_SRGB
- ColorSpaces.Ntsc1953 -> Named.NTSC_1953
- ColorSpaces.ProPhotoRgb -> Named.PRO_PHOTO_RGB
- ColorSpaces.SmpteC -> Named.SMPTE_C
- else -> Named.SRGB
- }
- return android.graphics.ColorSpace.get(frameworkNamedSpace)
- }
-
- @DoNotInline
- @JvmStatic
- fun android.graphics.ColorSpace.composeColorSpace(): ColorSpace {
- return when (this.id) {
- Named.SRGB.ordinal -> ColorSpaces.Srgb
- Named.ACES.ordinal -> ColorSpaces.Aces
- Named.ACESCG.ordinal -> ColorSpaces.Acescg
- Named.ADOBE_RGB.ordinal -> ColorSpaces.AdobeRgb
- Named.BT2020.ordinal -> ColorSpaces.Bt2020
- Named.BT709.ordinal -> ColorSpaces.Bt709
- Named.CIE_LAB.ordinal -> ColorSpaces.CieLab
- Named.CIE_XYZ.ordinal -> ColorSpaces.CieXyz
- Named.DCI_P3.ordinal -> ColorSpaces.DciP3
- Named.DISPLAY_P3.ordinal -> ColorSpaces.DisplayP3
- Named.EXTENDED_SRGB.ordinal -> ColorSpaces.ExtendedSrgb
- Named.LINEAR_EXTENDED_SRGB.ordinal -> ColorSpaces.LinearExtendedSrgb
- Named.LINEAR_SRGB.ordinal -> ColorSpaces.LinearSrgb
- Named.NTSC_1953.ordinal -> ColorSpaces.Ntsc1953
- Named.PRO_PHOTO_RGB.ordinal -> ColorSpaces.ProPhotoRgb
- Named.SMPTE_C.ordinal -> ColorSpaces.SmpteC
- else -> {
- if (this is android.graphics.ColorSpace.Rgb) {
- val transferParams = this.transferParameters
- val whitePoint = if (this.whitePoint.size == 3) {
- WhitePoint(this.whitePoint[0], this.whitePoint[1], this.whitePoint[2])
- } else {
- WhitePoint(this.whitePoint[0], this.whitePoint[1])
- }
-
- val composeTransferParams = if (transferParams != null) {
- TransferParameters(
- gamma = transferParams.g,
- a = transferParams.a,
- b = transferParams.b,
- c = transferParams.c,
- d = transferParams.d,
- e = transferParams.e,
- f = transferParams.f
- )
- } else {
- null
- }
- Rgb(
- name = this.name,
- primaries = this.primaries,
- whitePoint = whitePoint,
- transform = this.transform,
- oetf = { x -> this.oetf.applyAsDouble(x) },
- eotf = { x -> this.eotf.applyAsDouble(x) },
- min = this.getMinValue(0),
- max = this.getMaxValue(0),
- transferParameters = composeTransferParams,
- id = this.id
- )
- } else {
- ColorSpaces.Srgb
- }
- }
- }
- }
+ colorSpace?.toComposeColorSpace() ?: ColorSpaces.Srgb
}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPath.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPath.android.kt
index 18705bc..f4272bb 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPath.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPath.android.kt
@@ -190,6 +190,11 @@
internalPath.transform(mMatrix)
}
+ override fun transform(matrix: Matrix) {
+ mMatrix.setFrom(matrix)
+ internalPath.transform(mMatrix)
+ }
+
override fun getBounds(): Rect {
internalPath.computeBounds(rectF, true)
return Rect(
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Brush.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Brush.kt
index bda9615..5f7a1d0 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Brush.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Brush.kt
@@ -57,7 +57,8 @@
* )
* ```
*
- * @see androidx.compose.ui.graphics.samples.GradientBrushSample
+ * @sample androidx.compose.ui.graphics.samples.LinearGradientColorStopSample
+ * @sample androidx.compose.ui.graphics.samples.GradientBrushSample
*
* @param colorStops Colors and their offset in the gradient area
* @param start Starting position of the linear gradient. This can be set to
@@ -88,11 +89,12 @@
* ```
* Brush.linearGradient(
* listOf(Color.Red, Color.Green, Color.Blue),
- * start = Offset(0.0f, 50.0f)
+ * start = Offset(0.0f, 50.0f),
* end = Offset(0.0f, 100.0f)
* )
* ```
*
+ * @sample androidx.compose.ui.graphics.samples.LinearGradientSample
* @sample androidx.compose.ui.graphics.samples.GradientBrushSample
*
* @param colors Colors to be rendered as part of the gradient
@@ -129,6 +131,7 @@
* )
* ```
*
+ * @sample androidx.compose.ui.graphics.samples.HorizontalGradientSample
* @sample androidx.compose.ui.graphics.samples.GradientBrushSample
*
* @param colors colors Colors to be rendered as part of the gradient
@@ -163,6 +166,7 @@
* )
* ```
*
+ * @sample androidx.compose.ui.graphics.samples.HorizontalGradientColorStopSample
* @sample androidx.compose.ui.graphics.samples.GradientBrushSample
*
* @param colorStops Colors and offsets to determine how the colors are dispersed throughout
@@ -197,9 +201,9 @@
* startY = 0.0f,
* endY = 100.0f
* )
- *
* ```
*
+ * @sample androidx.compose.ui.graphics.samples.VerticalGradientSample
* @sample androidx.compose.ui.graphics.samples.GradientBrushSample
*
* @param colors colors Colors to be rendered as part of the gradient
@@ -234,6 +238,7 @@
* )
* ```
*
+ * @sample androidx.compose.ui.graphics.samples.VerticalGradientColorStopSample
* @sample androidx.compose.ui.graphics.samples.GradientBrushSample
*
* @param colorStops Colors and offsets to determine how the colors are dispersed throughout
@@ -273,6 +278,7 @@
* )
* ```
*
+ * @sample androidx.compose.ui.graphics.samples.RadialBrushColorStopSample
* @sample androidx.compose.ui.graphics.samples.GradientBrushSample
*
* @param colorStops Colors and offsets to determine how the colors are dispersed throughout
@@ -305,13 +311,13 @@
* ```
* Brush.radialGradient(
* listOf(Color.Red, Color.Green, Color.Blue),
- * centerX = side1 / 2.0f,
- * centerY = side2 / 2.0f,
+ * center = Offset(side1 / 2.0f, side2 / 2.0f),
* radius = side1 / 2.0f,
* tileMode = TileMode.Repeated
* )
* ```
*
+ * @sample androidx.compose.ui.graphics.samples.RadialBrushSample
* @sample androidx.compose.ui.graphics.samples.GradientBrushSample
*
* @param colors Colors to be rendered as part of the gradient
@@ -353,6 +359,7 @@
* )
* ```
*
+ * @sample androidx.compose.ui.graphics.samples.SweepGradientColorStopSample
* @sample androidx.compose.ui.graphics.samples.GradientBrushSample
*
* @param colorStops Colors and offsets to determine how the colors are dispersed throughout
@@ -384,6 +391,7 @@
* )
* ```
*
+ * @sample androidx.compose.ui.graphics.samples.SweepGradientSample
* @sample androidx.compose.ui.graphics.samples.GradientBrushSample
*
* @param colors List of colors to fill the sweep gradient
@@ -645,8 +653,14 @@
final override fun applyTo(size: Size, p: Paint, alpha: Float) {
var shader = internalShader
if (shader == null || createdSize != size) {
- shader = createShader(size).also { internalShader = it }
- createdSize = size
+ if (size.isEmpty()) {
+ shader = null
+ internalShader = null
+ createdSize = Size.Unspecified
+ } else {
+ shader = createShader(size).also { internalShader = it }
+ createdSize = size
+ }
}
if (p.color != Color.Black) p.color = Color.Black
if (p.shader != shader) p.shader = shader
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt
index ff000d5..ca53498 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt
@@ -656,3 +656,17 @@
* is returned.
*/
inline fun Color.takeOrElse(block: () -> Color): Color = if (isSpecified) this else block()
+
+/**
+ * Alternative to `() -> Color` that's useful for avoiding boxing.
+ *
+ * Can be used as:
+ *
+ * fun nonBoxedArgs(color: ColorProducer?)
+ */
+fun interface ColorProducer {
+ /**
+ * Return the color
+ */
+ fun invoke(): Color
+}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt
index b12273e..e54eef0 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt
@@ -233,6 +233,13 @@
fun translate(offset: Offset)
/**
+ * Transform the points in this path by the provided matrix
+ */
+ fun transform(matrix: Matrix) {
+ // NO-OP to ensure runtime + compile time compatibility
+ }
+
+ /**
* Compute the bounds of the control points of the path, and write the
* answer into bounds. If the path contains 0 or 1 points, the bounds is
* set to (0,0,0,0)
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt
index 66f1e24e..b4e0ed9 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt
@@ -49,27 +49,15 @@
internal val EmptyArray = FloatArray(0)
class PathParser {
- private data class PathPoint(var x: Float = 0.0f, var y: Float = 0.0f) {
- fun reset() {
- x = 0.0f
- y = 0.0f
- }
- }
-
private val nodes = mutableListOf<PathNode>()
+ private val floatResult = FloatResult()
+ private var nodeData = FloatArray(64)
+
fun clear() {
nodes.clear()
}
- private val currentPoint = PathPoint()
- private val ctrlPoint = PathPoint()
- private val segmentPoint = PathPoint()
- private val reflectiveCtrlPoint = PathPoint()
-
- private val floatResult = FloatResult()
- private var nodeData = FloatArray(64)
-
/**
* Parses the path string to create a collection of PathNode instances with their corresponding
* arguments
@@ -151,432 +139,414 @@
return this
}
- fun toNodes(): List<PathNode> = nodes
+ fun toNodes() = nodes
- fun toPath(target: Path = Path()): Path {
- target.reset()
- currentPoint.reset()
- ctrlPoint.reset()
- segmentPoint.reset()
- reflectiveCtrlPoint.reset()
-
- var previousNode: PathNode? = null
- nodes.fastForEach { node ->
- if (previousNode == null) previousNode = node
- when (node) {
- is Close -> close(target)
- is RelativeMoveTo -> node.relativeMoveTo(target)
- is MoveTo -> node.moveTo(target)
- is RelativeLineTo -> node.relativeLineTo(target)
- is LineTo -> node.lineTo(target)
- is RelativeHorizontalTo -> node.relativeHorizontalTo(target)
- is HorizontalTo -> node.horizontalTo(target)
- is RelativeVerticalTo -> node.relativeVerticalTo(target)
- is VerticalTo -> node.verticalTo(target)
- is RelativeCurveTo -> node.relativeCurveTo(target)
- is CurveTo -> node.curveTo(target)
- is RelativeReflectiveCurveTo ->
- node.relativeReflectiveCurveTo(previousNode!!.isCurve, target)
- is ReflectiveCurveTo -> node.reflectiveCurveTo(previousNode!!.isCurve, target)
- is RelativeQuadTo -> node.relativeQuadTo(target)
- is QuadTo -> node.quadTo(target)
- is RelativeReflectiveQuadTo ->
- node.relativeReflectiveQuadTo(previousNode!!.isQuad, target)
- is ReflectiveQuadTo -> node.reflectiveQuadTo(previousNode!!.isQuad, target)
- is RelativeArcTo -> node.relativeArcTo(target)
- is ArcTo -> node.arcTo(target)
- }
- previousNode = node
- }
- return target
- }
-
- private fun close(target: Path) {
- currentPoint.x = segmentPoint.x
- currentPoint.y = segmentPoint.y
- ctrlPoint.x = segmentPoint.x
- ctrlPoint.y = segmentPoint.y
-
- target.close()
- target.moveTo(currentPoint.x, currentPoint.y)
- }
-
- private fun RelativeMoveTo.relativeMoveTo(target: Path) {
- currentPoint.x += dx
- currentPoint.y += dy
- target.relativeMoveTo(dx, dy)
- segmentPoint.x = currentPoint.x
- segmentPoint.y = currentPoint.y
- }
-
- private fun MoveTo.moveTo(target: Path) {
- currentPoint.x = x
- currentPoint.y = y
- target.moveTo(x, y)
- segmentPoint.x = currentPoint.x
- segmentPoint.y = currentPoint.y
- }
-
- private fun RelativeLineTo.relativeLineTo(target: Path) {
- target.relativeLineTo(dx, dy)
- currentPoint.x += dx
- currentPoint.y += dy
- }
-
- private fun LineTo.lineTo(target: Path) {
- target.lineTo(x, y)
- currentPoint.x = x
- currentPoint.y = y
- }
-
- private fun RelativeHorizontalTo.relativeHorizontalTo(target: Path) {
- target.relativeLineTo(dx, 0.0f)
- currentPoint.x += dx
- }
-
- private fun HorizontalTo.horizontalTo(target: Path) {
- target.lineTo(x, currentPoint.y)
- currentPoint.x = x
- }
-
- private fun RelativeVerticalTo.relativeVerticalTo(target: Path) {
- target.relativeLineTo(0.0f, dy)
- currentPoint.y += dy
- }
-
- private fun VerticalTo.verticalTo(target: Path) {
- target.lineTo(currentPoint.x, y)
- currentPoint.y = y
- }
-
- private fun RelativeCurveTo.relativeCurveTo(target: Path) {
- target.relativeCubicTo(
- dx1, dy1,
- dx2, dy2,
- dx3, dy3
- )
- ctrlPoint.x = currentPoint.x + dx2
- ctrlPoint.y = currentPoint.y + dy2
- currentPoint.x += dx3
- currentPoint.y += dy3
- }
-
- private fun CurveTo.curveTo(target: Path) {
- target.cubicTo(
- x1, y1,
- x2, y2,
- x3, y3
- )
- ctrlPoint.x = x2
- ctrlPoint.y = y2
- currentPoint.x = x3
- currentPoint.y = y3
- }
-
- private fun RelativeReflectiveCurveTo.relativeReflectiveCurveTo(
- prevIsCurve: Boolean,
- target: Path
- ) {
- if (prevIsCurve) {
- reflectiveCtrlPoint.x = currentPoint.x - ctrlPoint.x
- reflectiveCtrlPoint.y = currentPoint.y - ctrlPoint.y
- } else {
- reflectiveCtrlPoint.reset()
- }
-
- target.relativeCubicTo(
- reflectiveCtrlPoint.x, reflectiveCtrlPoint.y,
- dx1, dy1,
- dx2, dy2
- )
- ctrlPoint.x = currentPoint.x + dx1
- ctrlPoint.y = currentPoint.y + dy1
- currentPoint.x += dx2
- currentPoint.y += dy2
- }
-
- private fun ReflectiveCurveTo.reflectiveCurveTo(prevIsCurve: Boolean, target: Path) {
- if (prevIsCurve) {
- reflectiveCtrlPoint.x = 2 * currentPoint.x - ctrlPoint.x
- reflectiveCtrlPoint.y = 2 * currentPoint.y - ctrlPoint.y
- } else {
- reflectiveCtrlPoint.x = currentPoint.x
- reflectiveCtrlPoint.y = currentPoint.y
- }
-
- target.cubicTo(
- reflectiveCtrlPoint.x, reflectiveCtrlPoint.y,
- x1, y1, x2, y2
- )
- ctrlPoint.x = x1
- ctrlPoint.y = y1
- currentPoint.x = x2
- currentPoint.y = y2
- }
-
- private fun RelativeQuadTo.relativeQuadTo(target: Path) {
- target.relativeQuadraticBezierTo(dx1, dy1, dx2, dy2)
- ctrlPoint.x = currentPoint.x + dx1
- ctrlPoint.y = currentPoint.y + dy1
- currentPoint.x += dx2
- currentPoint.y += dy2
- }
-
- private fun QuadTo.quadTo(target: Path) {
- target.quadraticBezierTo(x1, y1, x2, y2)
- ctrlPoint.x = x1
- ctrlPoint.y = y1
- currentPoint.x = x2
- currentPoint.y = y2
- }
-
- private fun RelativeReflectiveQuadTo.relativeReflectiveQuadTo(
- prevIsQuad: Boolean,
- target: Path
- ) {
- if (prevIsQuad) {
- reflectiveCtrlPoint.x = currentPoint.x - ctrlPoint.x
- reflectiveCtrlPoint.y = currentPoint.y - ctrlPoint.y
- } else {
- reflectiveCtrlPoint.reset()
- }
-
- target.relativeQuadraticBezierTo(
- reflectiveCtrlPoint.x,
- reflectiveCtrlPoint.y, dx, dy
- )
- ctrlPoint.x = currentPoint.x + reflectiveCtrlPoint.x
- ctrlPoint.y = currentPoint.y + reflectiveCtrlPoint.y
- currentPoint.x += dx
- currentPoint.y += dy
- }
-
- private fun ReflectiveQuadTo.reflectiveQuadTo(prevIsQuad: Boolean, target: Path) {
- if (prevIsQuad) {
- reflectiveCtrlPoint.x = 2 * currentPoint.x - ctrlPoint.x
- reflectiveCtrlPoint.y = 2 * currentPoint.y - ctrlPoint.y
- } else {
- reflectiveCtrlPoint.x = currentPoint.x
- reflectiveCtrlPoint.y = currentPoint.y
- }
- target.quadraticBezierTo(
- reflectiveCtrlPoint.x,
- reflectiveCtrlPoint.y, x, y
- )
- ctrlPoint.x = reflectiveCtrlPoint.x
- ctrlPoint.y = reflectiveCtrlPoint.y
- currentPoint.x = x
- currentPoint.y = y
- }
-
- private fun RelativeArcTo.relativeArcTo(target: Path) {
- val arcStartX = arcStartDx + currentPoint.x
- val arcStartY = arcStartDy + currentPoint.y
-
- drawArc(
- target,
- currentPoint.x.toDouble(),
- currentPoint.y.toDouble(),
- arcStartX.toDouble(),
- arcStartY.toDouble(),
- horizontalEllipseRadius.toDouble(),
- verticalEllipseRadius.toDouble(),
- theta.toDouble(),
- isMoreThanHalf,
- isPositiveArc
- )
- currentPoint.x = arcStartX
- currentPoint.y = arcStartY
-
- ctrlPoint.x = currentPoint.x
- ctrlPoint.y = currentPoint.y
- }
-
- private fun ArcTo.arcTo(target: Path) {
- drawArc(
- target,
- currentPoint.x.toDouble(),
- currentPoint.y.toDouble(),
- arcStartX.toDouble(),
- arcStartY.toDouble(),
- horizontalEllipseRadius.toDouble(),
- verticalEllipseRadius.toDouble(),
- theta.toDouble(),
- isMoreThanHalf,
- isPositiveArc
- )
-
- currentPoint.x = arcStartX
- currentPoint.y = arcStartY
-
- ctrlPoint.x = currentPoint.x
- ctrlPoint.y = currentPoint.y
- }
-
- private fun drawArc(
- p: Path,
- x0: Double,
- y0: Double,
- x1: Double,
- y1: Double,
- a: Double,
- b: Double,
- theta: Double,
- isMoreThanHalf: Boolean,
- isPositiveArc: Boolean
- ) {
-
- /* Convert rotation angle from degrees to radians */
- val thetaD = theta.toRadians()
- /* Pre-compute rotation matrix entries */
- val cosTheta = cos(thetaD)
- val sinTheta = sin(thetaD)
- /* Transform (x0, y0) and (x1, y1) into unit space */
- /* using (inverse) rotation, followed by (inverse) scale */
- val x0p = (x0 * cosTheta + y0 * sinTheta) / a
- val y0p = (-x0 * sinTheta + y0 * cosTheta) / b
- val x1p = (x1 * cosTheta + y1 * sinTheta) / a
- val y1p = (-x1 * sinTheta + y1 * cosTheta) / b
-
- /* Compute differences and averages */
- val dx = x0p - x1p
- val dy = y0p - y1p
- val xm = (x0p + x1p) / 2
- val ym = (y0p + y1p) / 2
- /* Solve for intersecting unit circles */
- val dsq = dx * dx + dy * dy
- if (dsq == 0.0) {
- return /* Points are coincident */
- }
- val disc = 1.0 / dsq - 1.0 / 4.0
- if (disc < 0.0) {
- val adjust = (sqrt(dsq) / 1.99999).toFloat()
- drawArc(
- p, x0, y0, x1, y1, a * adjust,
- b * adjust, theta, isMoreThanHalf, isPositiveArc
- )
- return /* Points are too far apart */
- }
- val s = sqrt(disc)
- val sdx = s * dx
- val sdy = s * dy
- var cx: Double
- var cy: Double
- if (isMoreThanHalf == isPositiveArc) {
- cx = xm - sdy
- cy = ym + sdx
- } else {
- cx = xm + sdy
- cy = ym - sdx
- }
-
- val eta0 = atan2(y0p - cy, x0p - cx)
-
- val eta1 = atan2(y1p - cy, x1p - cx)
-
- var sweep = eta1 - eta0
- if (isPositiveArc != (sweep >= 0)) {
- if (sweep > 0) {
- sweep -= 2 * PI
- } else {
- sweep += 2 * PI
- }
- }
-
- cx *= a
- cy *= b
- val tcx = cx
- cx = cx * cosTheta - cy * sinTheta
- cy = tcx * sinTheta + cy * cosTheta
-
- arcToBezier(
- p, cx, cy, a, b, x0, y0, thetaD,
- eta0, sweep
- )
- }
-
- /**
- * Converts an arc to cubic Bezier segments and records them in p.
- *
- * @param p The target for the cubic Bezier segments
- * @param cx The x coordinate center of the ellipse
- * @param cy The y coordinate center of the ellipse
- * @param a The radius of the ellipse in the horizontal direction
- * @param b The radius of the ellipse in the vertical direction
- * @param e1x E(eta1) x coordinate of the starting point of the arc
- * @param e1y E(eta2) y coordinate of the starting point of the arc
- * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
- * @param start The start angle of the arc on the ellipse
- * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
- */
- private fun arcToBezier(
- p: Path,
- cx: Double,
- cy: Double,
- a: Double,
- b: Double,
- e1x: Double,
- e1y: Double,
- theta: Double,
- start: Double,
- sweep: Double
- ) {
- var eta1x = e1x
- var eta1y = e1y
- // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
- // and http://www.spaceroots.org/documents/ellipse/node22.html
-
- // Maximum of 45 degrees per cubic Bezier segment
- val numSegments = ceil(abs(sweep * 4 / PI)).toInt()
-
- var eta1 = start
- val cosTheta = cos(theta)
- val sinTheta = sin(theta)
- val cosEta1 = cos(eta1)
- val sinEta1 = sin(eta1)
- var ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1)
- var ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1)
-
- val anglePerSegment = sweep / numSegments
- for (i in 0 until numSegments) {
- val eta2 = eta1 + anglePerSegment
- val sinEta2 = sin(eta2)
- val cosEta2 = cos(eta2)
- val e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2)
- val e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2)
- val ep2x = (-a * cosTheta * sinEta2) - (b * sinTheta * cosEta2)
- val ep2y = (-a * sinTheta * sinEta2) + (b * cosTheta * cosEta2)
- val tanDiff2 = tan((eta2 - eta1) / 2)
- val alpha = sin(eta2 - eta1) * (sqrt(4 + 3.0 * tanDiff2 * tanDiff2) - 1) / 3
- val q1x = eta1x + alpha * ep1x
- val q1y = eta1y + alpha * ep1y
- val q2x = e2x - alpha * ep2x
- val q2y = e2y - alpha * ep2y
-
- // TODO (njawad) figure out if this is still necessary?
- // Adding this no-op call to workaround a proguard related issue.
- // p.relativeLineTo(0.0, 0.0)
-
- p.cubicTo(
- q1x.toFloat(),
- q1y.toFloat(),
- q2x.toFloat(),
- q2y.toFloat(),
- e2x.toFloat(),
- e2y.toFloat()
- )
- eta1 = eta2
- eta1x = e2x
- eta1y = e2y
- ep1x = ep2x
- ep1y = ep2y
- }
- }
+ fun toPath(target: Path = Path()) = nodes.toPath(target)
@Suppress("NOTHING_TO_INLINE")
private inline fun addNodes(cmd: Char, args: FloatArray, count: Int) {
cmd.addPathNodes(nodes, args, count)
}
-
- private fun Double.toRadians(): Double = this / 180 * PI
}
+
+/**
+ * Converts this list of [PathNode] into a [Path] by adding the appropriate
+ * commands to the [target] path. If [target] is not specified, a new
+ * [Path] instance is created. This method returns [target] or the newly
+ * created [Path].
+ */
+fun List<PathNode>.toPath(target: Path = Path()): Path {
+ // Rewind unsets the fill type so reset it here
+ val fillType = target.fillType
+ target.rewind()
+ target.fillType = fillType
+
+ var currentX = 0.0f
+ var currentY = 0.0f
+ var ctrlX = 0.0f
+ var ctrlY = 0.0f
+ var segmentX = 0.0f
+ var segmentY = 0.0f
+ var reflectiveCtrlX: Float
+ var reflectiveCtrlY: Float
+
+ var previousNode = if (isEmpty()) Close else this[0]
+ fastForEach { node ->
+ when (node) {
+ is Close -> {
+ currentX = segmentX
+ currentY = segmentY
+ ctrlX = segmentX
+ ctrlY = segmentY
+ target.close()
+ target.moveTo(currentX, currentY)
+ }
+
+ is RelativeMoveTo -> {
+ currentX += node.dx
+ currentY += node.dy
+ target.relativeMoveTo(node.dx, node.dy)
+ segmentX = currentX
+ segmentY = currentY
+ }
+
+ is MoveTo -> {
+ currentX = node.x
+ currentY = node.y
+ target.moveTo(node.x, node.y)
+ segmentX = currentX
+ segmentY = currentY
+ }
+
+ is RelativeLineTo -> {
+ target.relativeLineTo(node.dx, node.dy)
+ currentX += node.dx
+ currentY += node.dy
+ }
+
+ is LineTo -> {
+ target.lineTo(node.x, node.y)
+ currentX = node.x
+ currentY = node.y
+ }
+
+ is RelativeHorizontalTo -> {
+ target.relativeLineTo(node.dx, 0.0f)
+ currentX += node.dx
+ }
+
+ is HorizontalTo -> {
+ target.lineTo(node.x, currentY)
+ currentX = node.x
+ }
+
+ is RelativeVerticalTo -> {
+ target.relativeLineTo(0.0f, node.dy)
+ currentY += node.dy
+ }
+
+ is VerticalTo -> {
+ target.lineTo(currentX, node.y)
+ currentY = node.y
+ }
+
+ is RelativeCurveTo -> {
+ target.relativeCubicTo(
+ node.dx1, node.dy1,
+ node.dx2, node.dy2,
+ node.dx3, node.dy3
+ )
+ ctrlX = currentX + node.dx2
+ ctrlY = currentY + node.dy2
+ currentX += node.dx3
+ currentY += node.dy3
+ }
+
+ is CurveTo -> {
+ target.cubicTo(
+ node.x1, node.y1,
+ node.x2, node.y2,
+ node.x3, node.y3
+ )
+ ctrlX = node.x2
+ ctrlY = node.y2
+ currentX = node.x3
+ currentY = node.y3
+ }
+
+ is RelativeReflectiveCurveTo -> {
+ if (previousNode.isCurve) {
+ reflectiveCtrlX = currentX - ctrlX
+ reflectiveCtrlY = currentY - ctrlY
+ } else {
+ reflectiveCtrlX = 0.0f
+ reflectiveCtrlY = 0.0f
+ }
+ target.relativeCubicTo(
+ reflectiveCtrlX, reflectiveCtrlY,
+ node.dx1, node.dy1,
+ node.dx2, node.dy2
+ )
+ ctrlX = currentX + node.dx1
+ ctrlY = currentY + node.dy1
+ currentX += node.dx2
+ currentY += node.dy2
+ }
+
+ is ReflectiveCurveTo -> {
+ if (previousNode.isCurve) {
+ reflectiveCtrlX = 2 * currentX - ctrlX
+ reflectiveCtrlY = 2 * currentY - ctrlY
+ } else {
+ reflectiveCtrlX = currentX
+ reflectiveCtrlY = currentY
+ }
+ target.cubicTo(
+ reflectiveCtrlX, reflectiveCtrlY,
+ node.x1, node.y1, node.x2, node.y2
+ )
+ ctrlX = node.x1
+ ctrlY = node.y1
+ currentX = node.x2
+ currentY = node.y2
+ }
+
+ is RelativeQuadTo -> {
+ target.relativeQuadraticBezierTo(node.dx1, node.dy1, node.dx2, node.dy2)
+ ctrlX = currentX + node.dx1
+ ctrlY = currentY + node.dy1
+ currentX += node.dx2
+ currentY += node.dy2
+ }
+
+ is QuadTo -> {
+ target.quadraticBezierTo(node.x1, node.y1, node.x2, node.y2)
+ ctrlX = node.x1
+ ctrlY = node.y1
+ currentX = node.x2
+ currentY = node.y2
+ }
+
+ is RelativeReflectiveQuadTo -> {
+ if (previousNode.isQuad) {
+ reflectiveCtrlX = currentX - ctrlX
+ reflectiveCtrlY = currentY - ctrlY
+ } else {
+ reflectiveCtrlX = 0.0f
+ reflectiveCtrlY = 0.0f
+ }
+ target.relativeQuadraticBezierTo(
+ reflectiveCtrlX,
+ reflectiveCtrlY, node.dx, node.dy
+ )
+ ctrlX = currentX + reflectiveCtrlX
+ ctrlY = currentY + reflectiveCtrlY
+ currentX += node.dx
+ currentY += node.dy
+ }
+
+ is ReflectiveQuadTo -> {
+ if (previousNode.isQuad) {
+ reflectiveCtrlX = 2 * currentX - ctrlX
+ reflectiveCtrlY = 2 * currentY - ctrlY
+ } else {
+ reflectiveCtrlX = currentX
+ reflectiveCtrlY = currentY
+ }
+ target.quadraticBezierTo(
+ reflectiveCtrlX,
+ reflectiveCtrlY, node.x, node.y
+ )
+ ctrlX = reflectiveCtrlX
+ ctrlY = reflectiveCtrlY
+ currentX = node.x
+ currentY = node.y
+ }
+
+ is RelativeArcTo -> {
+ val arcStartX = node.arcStartDx + currentX
+ val arcStartY = node.arcStartDy + currentY
+ drawArc(
+ target,
+ currentX.toDouble(),
+ currentY.toDouble(),
+ arcStartX.toDouble(),
+ arcStartY.toDouble(),
+ node.horizontalEllipseRadius.toDouble(),
+ node.verticalEllipseRadius.toDouble(),
+ node.theta.toDouble(),
+ node.isMoreThanHalf,
+ node.isPositiveArc
+ )
+ currentX = arcStartX
+ currentY = arcStartY
+ ctrlX = currentX
+ ctrlY = currentY
+ }
+
+ is ArcTo -> {
+ drawArc(
+ target,
+ currentX.toDouble(),
+ currentY.toDouble(),
+ node.arcStartX.toDouble(),
+ node.arcStartY.toDouble(),
+ node.horizontalEllipseRadius.toDouble(),
+ node.verticalEllipseRadius.toDouble(),
+ node.theta.toDouble(),
+ node.isMoreThanHalf,
+ node.isPositiveArc
+ )
+ currentX = node.arcStartX
+ currentY = node.arcStartY
+ ctrlX = currentX
+ ctrlY = currentY
+ }
+ }
+ previousNode = node
+ }
+ return target
+}
+
+private fun drawArc(
+ p: Path,
+ x0: Double,
+ y0: Double,
+ x1: Double,
+ y1: Double,
+ a: Double,
+ b: Double,
+ theta: Double,
+ isMoreThanHalf: Boolean,
+ isPositiveArc: Boolean
+) {
+
+ /* Convert rotation angle from degrees to radians */
+ val thetaD = theta.toRadians()
+ /* Pre-compute rotation matrix entries */
+ val cosTheta = cos(thetaD)
+ val sinTheta = sin(thetaD)
+ /* Transform (x0, y0) and (x1, y1) into unit space */
+ /* using (inverse) rotation, followed by (inverse) scale */
+ val x0p = (x0 * cosTheta + y0 * sinTheta) / a
+ val y0p = (-x0 * sinTheta + y0 * cosTheta) / b
+ val x1p = (x1 * cosTheta + y1 * sinTheta) / a
+ val y1p = (-x1 * sinTheta + y1 * cosTheta) / b
+
+ /* Compute differences and averages */
+ val dx = x0p - x1p
+ val dy = y0p - y1p
+ val xm = (x0p + x1p) / 2
+ val ym = (y0p + y1p) / 2
+ /* Solve for intersecting unit circles */
+ val dsq = dx * dx + dy * dy
+ if (dsq == 0.0) {
+ return /* Points are coincident */
+ }
+ val disc = 1.0 / dsq - 1.0 / 4.0
+ if (disc < 0.0) {
+ val adjust = (sqrt(dsq) / 1.99999).toFloat()
+ drawArc(
+ p, x0, y0, x1, y1, a * adjust,
+ b * adjust, theta, isMoreThanHalf, isPositiveArc
+ )
+ return /* Points are too far apart */
+ }
+ val s = sqrt(disc)
+ val sdx = s * dx
+ val sdy = s * dy
+ var cx: Double
+ var cy: Double
+ if (isMoreThanHalf == isPositiveArc) {
+ cx = xm - sdy
+ cy = ym + sdx
+ } else {
+ cx = xm + sdy
+ cy = ym - sdx
+ }
+
+ val eta0 = atan2(y0p - cy, x0p - cx)
+
+ val eta1 = atan2(y1p - cy, x1p - cx)
+
+ var sweep = eta1 - eta0
+ if (isPositiveArc != (sweep >= 0)) {
+ if (sweep > 0) {
+ sweep -= 2 * PI
+ } else {
+ sweep += 2 * PI
+ }
+ }
+
+ cx *= a
+ cy *= b
+ val tcx = cx
+ cx = cx * cosTheta - cy * sinTheta
+ cy = tcx * sinTheta + cy * cosTheta
+
+ arcToBezier(
+ p, cx, cy, a, b, x0, y0, thetaD,
+ eta0, sweep
+ )
+}
+
+/**
+ * Converts an arc to cubic Bezier segments and records them in p.
+ *
+ * @param p The target for the cubic Bezier segments
+ * @param cx The x coordinate center of the ellipse
+ * @param cy The y coordinate center of the ellipse
+ * @param a The radius of the ellipse in the horizontal direction
+ * @param b The radius of the ellipse in the vertical direction
+ * @param e1x E(eta1) x coordinate of the starting point of the arc
+ * @param e1y E(eta2) y coordinate of the starting point of the arc
+ * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
+ * @param start The start angle of the arc on the ellipse
+ * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
+ */
+private fun arcToBezier(
+ p: Path,
+ cx: Double,
+ cy: Double,
+ a: Double,
+ b: Double,
+ e1x: Double,
+ e1y: Double,
+ theta: Double,
+ start: Double,
+ sweep: Double
+) {
+ var eta1x = e1x
+ var eta1y = e1y
+ // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
+ // and http://www.spaceroots.org/documents/ellipse/node22.html
+
+ // Maximum of 45 degrees per cubic Bezier segment
+ val numSegments = ceil(abs(sweep * 4 / PI)).toInt()
+
+ var eta1 = start
+ val cosTheta = cos(theta)
+ val sinTheta = sin(theta)
+ val cosEta1 = cos(eta1)
+ val sinEta1 = sin(eta1)
+ var ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1)
+ var ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1)
+
+ val anglePerSegment = sweep / numSegments
+ for (i in 0 until numSegments) {
+ val eta2 = eta1 + anglePerSegment
+ val sinEta2 = sin(eta2)
+ val cosEta2 = cos(eta2)
+ val e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2)
+ val e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2)
+ val ep2x = (-a * cosTheta * sinEta2) - (b * sinTheta * cosEta2)
+ val ep2y = (-a * sinTheta * sinEta2) + (b * cosTheta * cosEta2)
+ val tanDiff2 = tan((eta2 - eta1) / 2)
+ val alpha = sin(eta2 - eta1) * (sqrt(4 + 3.0 * tanDiff2 * tanDiff2) - 1) / 3
+ val q1x = eta1x + alpha * ep1x
+ val q1y = eta1y + alpha * ep1y
+ val q2x = e2x - alpha * ep2x
+ val q2y = e2y - alpha * ep2y
+
+ // TODO (njawad) figure out if this is still necessary?
+ // Adding this no-op call to workaround a proguard related issue.
+ // p.relativeLineTo(0.0, 0.0)
+
+ p.cubicTo(
+ q1x.toFloat(),
+ q1y.toFloat(),
+ q2x.toFloat(),
+ q2y.toFloat(),
+ e2x.toFloat(),
+ e2y.toFloat()
+ )
+ eta1 = eta2
+ eta1x = e2x
+ eta1y = e2y
+ ep1x = ep2x
+ ep1y = ep2y
+ }
+}
+
+@Suppress("NOTHING_TO_INLINE")
+private inline fun Double.toRadians(): Double = this / 180 * PI
diff --git a/compose/ui/ui-graphics/src/commonTest/kotlin/androidx/compose/ui/graphics/vector/PathParserTest.kt b/compose/ui/ui-graphics/src/commonTest/kotlin/androidx/compose/ui/graphics/vector/PathParserTest.kt
index 92b808d..31ff184 100644
--- a/compose/ui/ui-graphics/src/commonTest/kotlin/androidx/compose/ui/graphics/vector/PathParserTest.kt
+++ b/compose/ui/ui-graphics/src/commonTest/kotlin/androidx/compose/ui/graphics/vector/PathParserTest.kt
@@ -19,6 +19,7 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.RoundRect
+import androidx.compose.ui.graphics.Matrix
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.PathFillType
import androidx.compose.ui.graphics.PathOperation
@@ -187,6 +188,10 @@
// NO-OP
}
+ override fun transform(matrix: Matrix) {
+ // NO-OP
+ }
+
override fun getBounds(): Rect = Rect.Zero
override fun op(path1: Path, path2: Path, operation: PathOperation): Boolean = false
diff --git a/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopPathTest.kt b/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopPathTest.kt
index 332b62b..4dc92b9 100644
--- a/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopPathTest.kt
+++ b/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopPathTest.kt
@@ -225,4 +225,35 @@
assertTrue(path.isEmpty)
}
+
+ @Test
+ fun testTransform() {
+ val width = 100
+ val height = 100
+ val image = ImageBitmap(width, height)
+ val canvas = Canvas(image)
+
+ val path = Path().apply {
+ addRect(Rect(0f, 0f, 50f, 50f))
+ transform(
+ Matrix().apply { translate(50f, 50f) }
+ )
+ }
+
+ val paint = Paint().apply { color = Color.Black }
+ canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
+ paint.color = Color.Red
+ canvas.drawPath(path, paint)
+
+ image.toPixelMap().apply {
+ assertEquals(Color.Black, this[width / 2 - 3, height / 2 - 3])
+ assertEquals(Color.Black, this[width / 2, height / 2 - 3])
+ assertEquals(Color.Black, this[width / 2 - 3, height / 2])
+
+ assertEquals(Color.Red, this[width / 2 + 2, height / 2 + 2])
+ assertEquals(Color.Red, this[width - 2, height / 2 + 2])
+ assertEquals(Color.Red, this[width - 2, height - 2])
+ assertEquals(Color.Red, this[width / 2 + 2, height - 2])
+ }
+ }
}
diff --git a/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt b/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt
index 23c7e7a..0e0f06e 100644
--- a/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt
+++ b/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt
@@ -32,7 +32,6 @@
import org.jetbrains.skia.ClipMode as SkClipMode
import org.jetbrains.skia.RRect as SkRRect
import org.jetbrains.skia.Rect as SkRect
-// Using skiko use as it has versions for all mpp platforms
import org.jetbrains.skia.impl.use
actual typealias NativeCanvas = org.jetbrains.skia.Canvas
diff --git a/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPath.skiko.kt b/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPath.skiko.kt
index 403e36e..0285785 100644
--- a/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPath.skiko.kt
+++ b/compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPath.skiko.kt
@@ -172,6 +172,10 @@
internalPath.transform(Matrix33.makeTranslate(offset.x, offset.y))
}
+ override fun transform(matrix: Matrix) {
+ internalPath.transform(Matrix33.makeTranslate(0f, 0f).apply { setFrom(matrix) })
+ }
+
override fun getBounds(): Rect {
val bounds = internalPath.bounds
return Rect(
@@ -209,4 +213,63 @@
override val isConvex: Boolean get() = internalPath.isConvex
override val isEmpty: Boolean get() = internalPath.isEmpty
+
+ fun Matrix33.setFrom(matrix: Matrix) {
+ require(
+ matrix[0, 2] == 0f &&
+ matrix[1, 2] == 0f &&
+ matrix[2, 2] == 1f &&
+ matrix[3, 2] == 0f &&
+ matrix[2, 0] == 0f &&
+ matrix[2, 1] == 0f &&
+ matrix[2, 3] == 0f
+ ) {
+ "Matrix33 does not support arbitrary transforms"
+ }
+
+ // We'll reuse the array used in Matrix to avoid allocation by temporarily
+ // setting it to the 3x3 matrix used by android.graphics.Matrix
+ // Store the values of the 4 x 4 matrix into temporary variables
+ // to be reset after the 3 x 3 matrix is configured
+ val scaleX = matrix.values[Matrix.ScaleX] // 0
+ val skewY = matrix.values[Matrix.SkewY] // 1
+ val v2 = matrix.values[2] // 2
+ val persp0 = matrix.values[Matrix.Perspective0] // 3
+ val skewX = matrix.values[Matrix.SkewX] // 4
+ val scaleY = matrix.values[Matrix.ScaleY] // 5
+ val v6 = matrix.values[6] // 6
+ val persp1 = matrix.values[Matrix.Perspective1] // 7
+ val v8 = matrix.values[8] // 8
+
+ val translateX = matrix.values[Matrix.TranslateX]
+ val translateY = matrix.values[Matrix.TranslateY]
+ val persp2 = matrix.values[Matrix.Perspective2]
+
+ val v = matrix.values
+
+ v[0] = scaleX // MSCALE_X = 0
+ v[1] = skewX // MSKEW_X = 1
+ v[2] = translateX // MTRANS_X = 2
+ v[3] = skewY // MSKEW_Y = 3
+ v[4] = scaleY // MSCALE_Y = 4
+ v[5] = translateY // MTRANS_Y
+ v[6] = persp0 // MPERSP_0 = 6
+ v[7] = persp1 // MPERSP_1 = 7
+ v[8] = persp2 // MPERSP_2 = 8
+
+ for (i in 0..8) {
+ mat[i] = v[i]
+ }
+
+ // Reset the values back after the android.graphics.Matrix is configured
+ v[Matrix.ScaleX] = scaleX // 0
+ v[Matrix.SkewY] = skewY // 1
+ v[2] = v2 // 2
+ v[Matrix.Perspective0] = persp0 // 3
+ v[Matrix.SkewX] = skewX // 4
+ v[Matrix.ScaleY] = scaleY // 5
+ v[6] = v6 // 6
+ v[Matrix.Perspective1] = persp1 // 7
+ v[8] = v8 // 8
+ }
}
\ No newline at end of file
diff --git a/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/PrintToStringTest.kt b/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/PrintToStringTest.kt
index f818fcc..5d24c91 100644
--- a/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/PrintToStringTest.kt
+++ b/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/PrintToStringTest.kt
@@ -129,10 +129,10 @@
|-Node #X at (l=X, t=X, r=X, b=X)px, Tag: 'box'
| [Disabled]
| |-Node #X at (l=X, t=X, r=X, b=X)px
- | Role = 'Button'
| Focused = 'false'
+ | Role = 'Button'
| Text = '[Button]'
- | Actions = [RequestFocus, OnClick, GetTextLayoutResult]
+ | Actions = [OnClick, RequestFocus, GetTextLayoutResult]
| MergeDescendants = 'true'
|-Node #X at (l=X, t=X, r=X, b=X)px
Text = '[Hello]'
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt
index 9b17b00..18e4696 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt
@@ -607,7 +607,7 @@
fun enqueueRotaryScrollVertically(verticalScrollPixels: Float) {
// TODO(b/214437966): figure out if ongoing scroll events need to be cancelled.
- rotaryInputState.enqueueRotaryScrollHorizontally(verticalScrollPixels)
+ rotaryInputState.enqueueRotaryScrollVertically(verticalScrollPixels)
}
private fun MouseInputState.enterHover() {
diff --git a/compose/ui/ui-text/api/current.txt b/compose/ui/ui-text/api/current.txt
index ce34992..09ac6ba 100644
--- a/compose/ui/ui-text/api/current.txt
+++ b/compose/ui/ui-text/api/current.txt
@@ -509,7 +509,7 @@
public final class TextRangeKt {
method public static long TextRange(int start, int end);
method public static long TextRange(int index);
- method public static long constrain(long, int minimumValue, int maximumValue);
+ method public static long coerceIn(long, int minimumValue, int maximumValue);
method public static String substring(CharSequence, long range);
}
@@ -550,6 +550,7 @@
method public androidx.compose.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
method public androidx.compose.ui.text.style.TextIndent? getTextIndent();
method public androidx.compose.ui.text.style.TextMotion? getTextMotion();
+ method public boolean hasSameDrawAffectingAttributes(androidx.compose.ui.text.TextStyle other);
method public boolean hasSameLayoutAffectingAttributes(androidx.compose.ui.text.TextStyle other);
method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(optional androidx.compose.ui.text.TextStyle? other);
method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.TextMotion? textMotion);
diff --git a/compose/ui/ui-text/api/public_plus_experimental_current.txt b/compose/ui/ui-text/api/public_plus_experimental_current.txt
index 78d1a82..4299328 100644
--- a/compose/ui/ui-text/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-text/api/public_plus_experimental_current.txt
@@ -522,7 +522,7 @@
public final class TextRangeKt {
method public static long TextRange(int start, int end);
method public static long TextRange(int index);
- method public static long constrain(long, int minimumValue, int maximumValue);
+ method public static long coerceIn(long, int minimumValue, int maximumValue);
method public static String substring(CharSequence, long range);
}
@@ -563,6 +563,7 @@
method public androidx.compose.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
method public androidx.compose.ui.text.style.TextIndent? getTextIndent();
method public androidx.compose.ui.text.style.TextMotion? getTextMotion();
+ method public boolean hasSameDrawAffectingAttributes(androidx.compose.ui.text.TextStyle other);
method public boolean hasSameLayoutAffectingAttributes(androidx.compose.ui.text.TextStyle other);
method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(optional androidx.compose.ui.text.TextStyle? other);
method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.TextMotion? textMotion);
diff --git a/compose/ui/ui-text/api/restricted_current.txt b/compose/ui/ui-text/api/restricted_current.txt
index ce34992..09ac6ba 100644
--- a/compose/ui/ui-text/api/restricted_current.txt
+++ b/compose/ui/ui-text/api/restricted_current.txt
@@ -509,7 +509,7 @@
public final class TextRangeKt {
method public static long TextRange(int start, int end);
method public static long TextRange(int index);
- method public static long constrain(long, int minimumValue, int maximumValue);
+ method public static long coerceIn(long, int minimumValue, int maximumValue);
method public static String substring(CharSequence, long range);
}
@@ -550,6 +550,7 @@
method public androidx.compose.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
method public androidx.compose.ui.text.style.TextIndent? getTextIndent();
method public androidx.compose.ui.text.style.TextMotion? getTextMotion();
+ method public boolean hasSameDrawAffectingAttributes(androidx.compose.ui.text.TextStyle other);
method public boolean hasSameLayoutAffectingAttributes(androidx.compose.ui.text.TextStyle other);
method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(optional androidx.compose.ui.text.TextStyle? other);
method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.TextMotion? textMotion);
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt
index 386b478..1aef72f 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt
@@ -1348,7 +1348,7 @@
val paragraph = simpleParagraph(
text = "",
style = TextStyle(brush = brush),
- width = 0.0f
+ width = 1.0f
)
assertThat(paragraph.textPaint.shader).isNotNull()
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextPainterTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextPainterTest.kt
index af0ce0e3..b847c51 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextPainterTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextPainterTest.kt
@@ -473,7 +473,7 @@
@Test
fun textMeasurerDraw_isConstrainedTo_canvasSizeByDefault() {
val measurer = textMeasurer()
- // constrain the width, height is ignored
+ // coerceIn the width, height is ignored
val textLayoutResult = measurer.measure(
text = longText,
style = TextStyle(
@@ -499,7 +499,7 @@
@Test
fun textMeasurerDraw_usesCanvasDensity_ByDefault() {
val measurer = textMeasurer()
- // constrain the width, height is ignored
+ // coerceIn the width, height is ignored
val textLayoutResult = measurer.measure(
text = longText,
style = TextStyle(
@@ -528,7 +528,7 @@
@Test
fun drawTextClipsTheContent_ifOverflowIsClip() {
val measurer = textMeasurer()
- // constrain the width, height is ignored
+ // coerceIn the width, height is ignored
val textLayoutResult = measurer.measure(
text = longText,
style = TextStyle(
@@ -588,7 +588,7 @@
@Test
fun drawTextDoesNotClipTheContent_ifOverflowIsVisible() {
val measurer = textMeasurer()
- // constrain the width, height is ignored
+ // coerceIn the width, height is ignored
val textLayoutResult = measurer.measure(
text = longText,
style = TextStyle(
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt
index 35b572e..cc2ed1d 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt
@@ -50,8 +50,8 @@
import androidx.compose.ui.text.android.LayoutCompat.DEFAULT_JUSTIFICATION_MODE
import androidx.compose.ui.text.android.LayoutCompat.DEFAULT_LINESPACING_MULTIPLIER
import androidx.compose.ui.text.android.LayoutCompat.HYPHENATION_FREQUENCY_NONE
-import androidx.compose.ui.text.android.LayoutCompat.HYPHENATION_FREQUENCY_NORMAL
-import androidx.compose.ui.text.android.LayoutCompat.HYPHENATION_FREQUENCY_NORMAL_FAST
+import androidx.compose.ui.text.android.LayoutCompat.HYPHENATION_FREQUENCY_FULL
+import androidx.compose.ui.text.android.LayoutCompat.HYPHENATION_FREQUENCY_FULL_FAST
import androidx.compose.ui.text.android.LayoutCompat.DEFAULT_LINE_BREAK_STYLE
import androidx.compose.ui.text.android.LayoutCompat.DEFAULT_LINE_BREAK_WORD_STYLE
import androidx.compose.ui.text.android.LayoutCompat.JUSTIFICATION_MODE_INTER_WORD
@@ -562,9 +562,9 @@
@OptIn(InternalPlatformTextApi::class)
private fun toLayoutHyphenationFrequency(hyphens: Hyphens?): Int = when (hyphens) {
Hyphens.Auto -> if (Build.VERSION.SDK_INT <= 32) {
- HYPHENATION_FREQUENCY_NORMAL
+ HYPHENATION_FREQUENCY_FULL
} else {
- HYPHENATION_FREQUENCY_NORMAL_FAST
+ HYPHENATION_FREQUENCY_FULL_FAST
}
Hyphens.None -> HYPHENATION_FREQUENCY_NONE
else -> DEFAULT_HYPHENATION_FREQUENCY
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/SpanStyle.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/SpanStyle.kt
index f024207..9a92316 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/SpanStyle.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/SpanStyle.kt
@@ -650,7 +650,7 @@
return true
}
- private fun hasSameNonLayoutAttributes(other: SpanStyle): Boolean {
+ internal fun hasSameNonLayoutAttributes(other: SpanStyle): Boolean {
if (textForegroundStyle != other.textForegroundStyle) return false
if (textDecoration != other.textDecoration) return false
if (shadow != other.shadow) return false
@@ -889,7 +889,7 @@
// any new vals should do a pre-merge check here
val requiresAlloc = fontSize.isSpecified && fontSize != this.fontSize ||
- brush == null && color != textForegroundStyle.color ||
+ brush == null && color.isSpecified && color != textForegroundStyle.color ||
fontStyle != null && fontStyle != this.fontStyle ||
fontWeight != null && fontWeight != this.fontWeight ||
// ref check for font-family, since we don't want to compare lists in fast path
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextRange.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextRange.kt
index fbad0c0..2fbf0a79 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextRange.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextRange.kt
@@ -108,7 +108,7 @@
* @param minimumValue the minimum value that [TextRange.start] or [TextRange.end] can be.
* @param maximumValue the exclusive maximum value that [TextRange.start] or [TextRange.end] can be.
*/
-fun TextRange.constrain(minimumValue: Int, maximumValue: Int): TextRange {
+fun TextRange.coerceIn(minimumValue: Int, maximumValue: Int): TextRange {
val newStart = start.coerceIn(minimumValue, maximumValue)
val newEnd = end.coerceIn(minimumValue, maximumValue)
if (newStart != start || newEnd != end) {
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt
index 1b715f9..c6b3a41 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt
@@ -515,7 +515,8 @@
/**
* Fast merge non-default values and parameters.
*
- * This is the same algorithm as [merge] but does not require allocating a [TextStyle] to call.
+ * This is the same algorithm as [merge] but does not require allocating it's parameter and may
+ * return this instead of allocating a result when all values are default.
*
* This is a similar algorithm to [copy] but when either this or a parameter are set to a
* default value, the other value will take precedent.
@@ -535,7 +536,7 @@
* Example 3:
* - this.color = [Color.Red]
* - [color] = [Color.Blue]
- * - result => [Color.Blue]]
+ * - result => [Color.Blue]
*
* You should _always_ use this method over the [merge]([TextStyle]) overload when you do not
* already have a TextStyle allocated. You should chose this over [copy] when building a theming
@@ -1143,6 +1144,10 @@
spanStyle.hasSameLayoutAffectingAttributes(other.spanStyle))
}
+ fun hasSameDrawAffectingAttributes(other: TextStyle): Boolean {
+ return (this === other) || (spanStyle.hasSameNonLayoutAttributes(other.spanStyle))
+ }
+
override fun hashCode(): Int {
var result = spanStyle.hashCode()
result = 31 * result + paragraphStyle.hashCode()
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextFieldValue.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextFieldValue.kt
index d8dbaaf..ed074ba 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextFieldValue.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextFieldValue.kt
@@ -22,7 +22,7 @@
import androidx.compose.ui.text.AnnotatedStringSaver
import androidx.compose.ui.text.Saver
import androidx.compose.ui.text.TextRange
-import androidx.compose.ui.text.constrain
+import androidx.compose.ui.text.coerceIn
import androidx.compose.ui.text.restore
import androidx.compose.ui.text.save
import kotlin.math.max
@@ -85,7 +85,7 @@
* The selection range. If the selection is collapsed, it represents cursor
* location. When selection range is out of bounds, it is constrained with the text length.
*/
- val selection: TextRange = selection.constrain(0, text.length)
+ val selection: TextRange = selection.coerceIn(0, text.length)
/**
* Composition range created by IME. If null, there is no composition range.
@@ -99,7 +99,7 @@
* composition by setting the value to null. Applying a composition will accept the changes
* that were still being composed by IME.
*/
- val composition: TextRange? = composition?.constrain(0, text.length)
+ val composition: TextRange? = composition?.coerceIn(0, text.length)
/**
* Returns a copy of the TextFieldValue.
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextRangeTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextRangeTest.kt
index 36fd1bc..9c47e02 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextRangeTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextRangeTest.kt
@@ -139,23 +139,23 @@
@Test
fun constrain_updates_start_end_if_required() {
- assertThat(TextRange(0, 4).constrain(1, 3)).isEqualTo(TextRange(1, 3))
+ assertThat(TextRange(0, 4).coerceIn(1, 3)).isEqualTo(TextRange(1, 3))
}
@Test
fun constrain_with_collapsed_min_max_returns_collapsed_values() {
- assertThat(TextRange(1, 2).constrain(2, 2)).isEqualTo(TextRange(2, 2))
- assertThat(TextRange(2, 3).constrain(2, 2)).isEqualTo(TextRange(2, 2))
+ assertThat(TextRange(1, 2).coerceIn(2, 2)).isEqualTo(TextRange(2, 2))
+ assertThat(TextRange(2, 3).coerceIn(2, 2)).isEqualTo(TextRange(2, 2))
}
@Test
fun constrain_min_max_greater_than_TextRange_values() {
- assertThat(TextRange(0, 4).constrain(5, 6)).isEqualTo(TextRange(5, 5))
+ assertThat(TextRange(0, 4).coerceIn(5, 6)).isEqualTo(TextRange(5, 5))
}
@Test
fun constrain_min_smaller_than_TextRange_values() {
- assertThat(TextRange(5, 6).constrain(0, 4)).isEqualTo(TextRange(4, 4))
+ assertThat(TextRange(5, 6).coerceIn(0, 4)).isEqualTo(TextRange(4, 4))
}
@Test
diff --git a/compose/ui/ui-tooling-preview/api/current.ignore b/compose/ui/ui-tooling-preview/api/current.ignore
new file mode 100644
index 0000000..71dfdb2
--- /dev/null
+++ b/compose/ui/ui-tooling-preview/api/current.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+ChangedValue: androidx.compose.ui.tooling.preview.Devices#DESKTOP:
+ Field androidx.compose.ui.tooling.preview.Devices.DESKTOP has changed value from spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420 to spec:id=reference_desktop,shape=Normal,width=1920,height=1080,unit=dp,dpi=160
+ChangedValue: androidx.compose.ui.tooling.preview.Devices#FOLDABLE:
+ Field androidx.compose.ui.tooling.preview.Devices.FOLDABLE has changed value from spec:shape=Normal,width=673,height=841,unit=dp,dpi=480 to spec:id=reference_foldable,shape=Normal,width=673,height=841,unit=dp,dpi=420
+ChangedValue: androidx.compose.ui.tooling.preview.Devices#TABLET:
+ Field androidx.compose.ui.tooling.preview.Devices.TABLET has changed value from spec:shape=Normal,width=1280,height=800,unit=dp,dpi=420 to spec:id=reference_tablet,shape=Normal,width=1280,height=800,unit=dp,dpi=240
diff --git a/compose/ui/ui-tooling-preview/api/current.txt b/compose/ui/ui-tooling-preview/api/current.txt
index a5363d9..9d09eb2 100644
--- a/compose/ui/ui-tooling-preview/api/current.txt
+++ b/compose/ui/ui-tooling-preview/api/current.txt
@@ -4,8 +4,8 @@
public final class Devices {
field public static final String AUTOMOTIVE_1024p = "id:automotive_1024p_landscape";
field public static final String DEFAULT = "";
- field public static final String DESKTOP = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420";
- field public static final String FOLDABLE = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480";
+ field public static final String DESKTOP = "spec:id=reference_desktop,shape=Normal,width=1920,height=1080,unit=dp,dpi=160";
+ field public static final String FOLDABLE = "spec:id=reference_foldable,shape=Normal,width=673,height=841,unit=dp,dpi=420";
field public static final androidx.compose.ui.tooling.preview.Devices INSTANCE;
field public static final String NEXUS_10 = "name:Nexus 10";
field public static final String NEXUS_5 = "id:Nexus 5";
@@ -27,7 +27,7 @@
field public static final String PIXEL_4_XL = "id:pixel_4_xl";
field public static final String PIXEL_C = "id:pixel_c";
field public static final String PIXEL_XL = "id:pixel_xl";
- field public static final String TABLET = "spec:shape=Normal,width=1280,height=800,unit=dp,dpi=420";
+ field public static final String TABLET = "spec:id=reference_tablet,shape=Normal,width=1280,height=800,unit=dp,dpi=240";
field public static final String TV_1080p = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420";
field public static final String TV_720p = "spec:shape=Normal,width=1280,height=720,unit=dp,dpi=420";
field public static final String WEAR_OS_LARGE_ROUND = "id:wearos_large_round";
diff --git a/compose/ui/ui-tooling-preview/api/public_plus_experimental_current.txt b/compose/ui/ui-tooling-preview/api/public_plus_experimental_current.txt
index a5363d9..9d09eb2 100644
--- a/compose/ui/ui-tooling-preview/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-tooling-preview/api/public_plus_experimental_current.txt
@@ -4,8 +4,8 @@
public final class Devices {
field public static final String AUTOMOTIVE_1024p = "id:automotive_1024p_landscape";
field public static final String DEFAULT = "";
- field public static final String DESKTOP = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420";
- field public static final String FOLDABLE = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480";
+ field public static final String DESKTOP = "spec:id=reference_desktop,shape=Normal,width=1920,height=1080,unit=dp,dpi=160";
+ field public static final String FOLDABLE = "spec:id=reference_foldable,shape=Normal,width=673,height=841,unit=dp,dpi=420";
field public static final androidx.compose.ui.tooling.preview.Devices INSTANCE;
field public static final String NEXUS_10 = "name:Nexus 10";
field public static final String NEXUS_5 = "id:Nexus 5";
@@ -27,7 +27,7 @@
field public static final String PIXEL_4_XL = "id:pixel_4_xl";
field public static final String PIXEL_C = "id:pixel_c";
field public static final String PIXEL_XL = "id:pixel_xl";
- field public static final String TABLET = "spec:shape=Normal,width=1280,height=800,unit=dp,dpi=420";
+ field public static final String TABLET = "spec:id=reference_tablet,shape=Normal,width=1280,height=800,unit=dp,dpi=240";
field public static final String TV_1080p = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420";
field public static final String TV_720p = "spec:shape=Normal,width=1280,height=720,unit=dp,dpi=420";
field public static final String WEAR_OS_LARGE_ROUND = "id:wearos_large_round";
diff --git a/compose/ui/ui-tooling-preview/api/restricted_current.ignore b/compose/ui/ui-tooling-preview/api/restricted_current.ignore
new file mode 100644
index 0000000..71dfdb2
--- /dev/null
+++ b/compose/ui/ui-tooling-preview/api/restricted_current.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+ChangedValue: androidx.compose.ui.tooling.preview.Devices#DESKTOP:
+ Field androidx.compose.ui.tooling.preview.Devices.DESKTOP has changed value from spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420 to spec:id=reference_desktop,shape=Normal,width=1920,height=1080,unit=dp,dpi=160
+ChangedValue: androidx.compose.ui.tooling.preview.Devices#FOLDABLE:
+ Field androidx.compose.ui.tooling.preview.Devices.FOLDABLE has changed value from spec:shape=Normal,width=673,height=841,unit=dp,dpi=480 to spec:id=reference_foldable,shape=Normal,width=673,height=841,unit=dp,dpi=420
+ChangedValue: androidx.compose.ui.tooling.preview.Devices#TABLET:
+ Field androidx.compose.ui.tooling.preview.Devices.TABLET has changed value from spec:shape=Normal,width=1280,height=800,unit=dp,dpi=420 to spec:id=reference_tablet,shape=Normal,width=1280,height=800,unit=dp,dpi=240
diff --git a/compose/ui/ui-tooling-preview/api/restricted_current.txt b/compose/ui/ui-tooling-preview/api/restricted_current.txt
index a5363d9..9d09eb2 100644
--- a/compose/ui/ui-tooling-preview/api/restricted_current.txt
+++ b/compose/ui/ui-tooling-preview/api/restricted_current.txt
@@ -4,8 +4,8 @@
public final class Devices {
field public static final String AUTOMOTIVE_1024p = "id:automotive_1024p_landscape";
field public static final String DEFAULT = "";
- field public static final String DESKTOP = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420";
- field public static final String FOLDABLE = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480";
+ field public static final String DESKTOP = "spec:id=reference_desktop,shape=Normal,width=1920,height=1080,unit=dp,dpi=160";
+ field public static final String FOLDABLE = "spec:id=reference_foldable,shape=Normal,width=673,height=841,unit=dp,dpi=420";
field public static final androidx.compose.ui.tooling.preview.Devices INSTANCE;
field public static final String NEXUS_10 = "name:Nexus 10";
field public static final String NEXUS_5 = "id:Nexus 5";
@@ -27,7 +27,7 @@
field public static final String PIXEL_4_XL = "id:pixel_4_xl";
field public static final String PIXEL_C = "id:pixel_c";
field public static final String PIXEL_XL = "id:pixel_xl";
- field public static final String TABLET = "spec:shape=Normal,width=1280,height=800,unit=dp,dpi=420";
+ field public static final String TABLET = "spec:id=reference_tablet,shape=Normal,width=1280,height=800,unit=dp,dpi=240";
field public static final String TV_1080p = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420";
field public static final String TV_720p = "spec:shape=Normal,width=1280,height=720,unit=dp,dpi=420";
field public static final String WEAR_OS_LARGE_ROUND = "id:wearos_large_round";
diff --git a/compose/ui/ui-tooling-preview/build.gradle b/compose/ui/ui-tooling-preview/build.gradle
index a9f9236..d6d3a42 100644
--- a/compose/ui/ui-tooling-preview/build.gradle
+++ b/compose/ui/ui-tooling-preview/build.gradle
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("AndroidXPlugin")
@@ -25,47 +23,91 @@
id("com.android.library")
}
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
-dependencies {
- if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- implementation(libs.kotlinStdlib)
- api("androidx.annotation:annotation:1.2.0")
- api("androidx.compose.runtime:runtime:1.2.1")
- testImplementation(libs.junit)
- }
-}
+androidXMultiplatform {
+ android()
+ if (desktopEnabled) desktop()
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- androidXComposeMultiplatform {
- android()
- desktop()
- }
- kotlin {
- /*
- * When updating dependencies, make sure to make the an an analogous update in the
- * corresponding block above
- */
- sourceSets {
- commonMain.dependencies {
+ sourceSets {
+ commonMain {
+ dependencies {
implementation(libs.kotlinStdlibCommon)
api(project(":compose:runtime:runtime"))
}
+ }
- androidMain.dependencies {
+ commonTest {
+ dependencies {
+
+ }
+ }
+
+ jvmMain {
+ dependsOn(commonMain)
+ dependencies {
+ }
+ }
+
+ if (desktopEnabled) {
+ skikoMain {
+ dependsOn(commonMain)
+ dependencies {
+ api(project(":compose:runtime:runtime"))
+ }
+ }
+ }
+
+ androidMain {
+ dependsOn(jvmMain)
+ dependencies {
api("androidx.annotation:annotation:1.2.0")
}
+ }
- androidTest.dependencies {
+ if (desktopEnabled) {
+ desktopMain {
+ dependsOn(skikoMain)
+ dependsOn(jvmMain)
+ dependencies {
+
+ }
+ }
+ }
+
+ jvmTest {
+ dependsOn(commonTest)
+ dependencies {
+ }
+ }
+
+ androidAndroidTest {
+ dependsOn(jvmTest)
+ dependencies {
+ }
+ }
+
+ androidTest {
+ dependsOn(jvmTest)
+ dependencies {
implementation(libs.junit)
}
}
+
+ if (desktopEnabled) {
+ desktopTest {
+ dependsOn(jvmTest)
+ dependsOn(desktopMain)
+ dependencies {
+
+ }
+ }
+ }
}
}
-
androidx {
name = "Compose Tooling API"
type = LibraryType.PUBLISHED_LIBRARY
diff --git a/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/Device.kt b/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/Device.kt
index c6405e4..57fa29b 100644
--- a/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/Device.kt
+++ b/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/Device.kt
@@ -53,9 +53,11 @@
// Reference devices
const val PHONE = "spec:id=reference_phone,shape=Normal,width=411,height=891,unit=dp,dpi=420"
- const val FOLDABLE = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480"
- const val TABLET = "spec:shape=Normal,width=1280,height=800,unit=dp,dpi=420"
- const val DESKTOP = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420"
+ const val FOLDABLE =
+ "spec:id=reference_foldable,shape=Normal,width=673,height=841,unit=dp,dpi=420"
+ const val TABLET = "spec:id=reference_tablet,shape=Normal,width=1280,height=800,unit=dp,dpi=240"
+ const val DESKTOP =
+ "spec:id=reference_desktop,shape=Normal,width=1920,height=1080,unit=dp,dpi=160"
// TV devices (not adding 4K since it will be very heavy for preview)
const val TV_720p = "spec:shape=Normal,width=1280,height=720,unit=dp,dpi=420"
diff --git a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/PreviewActivity.kt b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/PreviewActivity.kt
index 5b64707..be6273d 100644
--- a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/PreviewActivity.kt
+++ b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/PreviewActivity.kt
@@ -27,7 +27,7 @@
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.runtime.currentComposer
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
@@ -107,7 +107,7 @@
// cycle through all the values.
if (previewParameters.size > 1) {
setContent {
- val index = remember { mutableStateOf(0) }
+ val index = remember { mutableIntStateOf(0) }
Scaffold(
content = { padding ->
diff --git a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/clock/Utils.kt b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/clock/Utils.kt
index ae1f769..0ecdc83 100644
--- a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/clock/Utils.kt
+++ b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/clock/Utils.kt
@@ -117,8 +117,10 @@
)
values[endTimeMs] = this.getValueFromNanos(millisToNanos(endTimeMs))
- for (millis in startTimeMs..endTimeMs step stepMs) {
+ var millis = startTimeMs
+ while (millis <= endTimeMs) {
values[millis] = this.getValueFromNanos(millisToNanos(millis))
+ millis += stepMs
}
values
}
@@ -145,8 +147,10 @@
)
values[endTimeMs] = this.animation.getValueFromNanos(millisToNanos(endTimeMs))
- for (millis in startTimeMs..endTimeMs step stepMs) {
+ var millis = startTimeMs
+ while (millis <= endTimeMs) {
values[millis] = this.animation.getValueFromNanos(millisToNanos(millis))
+ millis += stepMs
}
values
}
diff --git a/compose/ui/ui-unit/build.gradle b/compose/ui/ui-unit/build.gradle
index bfbfed7..627cd87 100644
--- a/compose/ui/ui-unit/build.gradle
+++ b/compose/ui/ui-unit/build.gradle
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("AndroidXPlugin")
@@ -24,85 +23,88 @@
id("AndroidXComposePlugin")
}
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
-if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- dependencies {
- /*
- * When updating dependencies, make sure to make the an an analogous update in the
- * corresponding block below
- */
+androidXMultiplatform {
+ android()
+ if (desktopEnabled) desktop()
- api(project(":compose:ui:ui-geometry"))
- api("androidx.annotation:annotation:1.1.0")
-
- implementation(libs.kotlinStdlib)
- implementation("androidx.compose.runtime:runtime:1.2.1")
- implementation(project(":compose:ui:ui-util"))
-
- testImplementation(libs.junit)
- testImplementation(libs.truth)
-
- androidTestImplementation(libs.testRules)
- androidTestImplementation(libs.testRunner)
- androidTestImplementation(libs.testExtJunit)
- androidTestImplementation(libs.espressoCore)
- androidTestImplementation(libs.truth)
- androidTestImplementation(libs.kotlinTest)
-
- samples(projectOrArtifact(":compose:ui:ui-unit:ui-unit-samples"))
- }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- androidXComposeMultiplatform {
- android()
- desktop()
- }
-
- kotlin {
-
- /*
- * When updating dependencies, make sure to make the an an analogous update in the
- * corresponding block above
- */
- sourceSets {
- commonMain.dependencies {
+ sourceSets {
+ commonMain {
+ dependencies {
implementation(libs.kotlinStdlibCommon)
api(project(":compose:ui:ui-geometry"))
implementation(project(":compose:runtime:runtime"))
implementation(project(":compose:ui:ui-util"))
}
- jvmMain.dependencies {
- implementation(libs.kotlinStdlib)
- }
- androidMain.dependencies {
- api("androidx.annotation:annotation:1.1.0")
- }
+ }
- commonTest.dependencies {
+ commonTest {
+ dependencies {
implementation(kotlin("test-junit"))
}
+ }
- // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
- // need to add Robolectric (which must be kept out of androidAndroidTest), use a top
- // level dependencies block instead:
- // `dependencies { testImplementation(libs.robolectric) }`
- androidTest.dependencies {
- implementation(libs.truth)
+ jvmMain {
+ dependencies {
+ implementation(libs.kotlinStdlib)
}
- androidAndroidTest.dependencies {
+ }
+
+
+ androidMain {
+ dependsOn(jvmMain)
+ dependencies {
+ api("androidx.annotation:annotation:1.1.0")
+ }
+ }
+
+ if (desktopEnabled) {
+ desktopMain {
+ dependsOn(jvmMain)
+ dependencies {
+ implementation(project(":compose:runtime:runtime"))
+ }
+ }
+ }
+
+ jvmTest {
+ dependencies {
+ }
+ }
+
+ androidAndroidTest {
+ dependsOn(jvmTest)
+ dependencies {
implementation(libs.testRules)
implementation(libs.testRunner)
implementation(libs.testExtJunit)
implementation(libs.espressoCore)
}
}
+
+ // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+ // need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+ // level dependencies block instead:
+ // `dependencies { testImplementation(libs.robolectric) }`
+ androidTest {
+ dependsOn(jvmTest)
+ dependencies {
+ implementation(libs.truth)
+ }
+ }
+
+ if (desktopEnabled) {
+ desktopTest {
+ dependsOn(jvmTest)
+ }
+ }
}
- dependencies {
- samples(projectOrArtifact(":compose:ui:ui-unit:ui-unit-samples"))
- }
+}
+
+dependencies {
+ samples(projectOrArtifact(":compose:ui:ui-unit:ui-unit-samples"))
}
androidx {
diff --git a/compose/ui/ui-util/build.gradle b/compose/ui/ui-util/build.gradle
index 8eeb75e..4c19723 100644
--- a/compose/ui/ui-util/build.gradle
+++ b/compose/ui/ui-util/build.gradle
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-import androidx.build.AndroidXComposePlugin
+import androidx.build.KmpPlatformsKt
import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("AndroidXPlugin")
@@ -24,59 +23,72 @@
id("AndroidXComposePlugin")
}
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
-if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- dependencies {
- /*
- * When updating dependencies, make sure to make the an an analogous update in the
- * corresponding block below
- */
+androidXMultiplatform {
+ android()
+ if (desktopEnabled) desktop()
- implementation(libs.kotlinStdlib)
-
- testImplementation(libs.junit)
- testImplementation(libs.truth)
- testImplementation(libs.kotlinTest)
- }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- androidXComposeMultiplatform {
- android()
- desktop()
- }
-
- kotlin {
- /*
- * When updating dependencies, make sure to make the an an analogous update in the
- * corresponding block above
- */
- sourceSets {
- commonMain.dependencies {
+ sourceSets {
+ commonMain {
+ dependencies {
implementation(libs.kotlinStdlibCommon)
}
+ }
- jvmMain.dependencies {
- implementation(libs.kotlinStdlib)
- }
-
- androidMain.dependencies {
- implementation(libs.kotlinStdlib)
- }
-
- commonTest.dependencies {
+ commonTest {
+ dependencies {
implementation(kotlin("test-junit"))
}
+ }
- // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
- // need to add Robolectric (which must be kept out of androidAndroidTest), use a top
- // level dependencies block instead:
- // `dependencies { testImplementation(libs.robolectric) }`
- androidTest.dependencies {
+ jvmMain {
+ dependencies {
+ implementation(libs.kotlinStdlib)
+ }
+ }
+
+
+ androidMain {
+ dependsOn(jvmMain)
+ dependencies {
+ implementation(libs.kotlinStdlib)
+ }
+ }
+
+ if (desktopEnabled) {
+ desktopMain {
+ dependsOn(jvmMain)
+ }
+ }
+
+ jvmTest {
+ dependencies {
+ }
+ }
+
+ androidAndroidTest {
+ dependsOn(jvmTest)
+ dependencies {
+ }
+ }
+
+ // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+ // need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+ // level dependencies block instead:
+ // `dependencies { testImplementation(libs.robolectric) }`
+ androidTest {
+ dependsOn(jvmTest)
+ dependencies {
implementation(libs.truth)
}
}
+
+ if (desktopEnabled) {
+ desktopTest {
+ dependsOn(jvmTest)
+ }
+ }
}
}
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 2584370..eeeb983 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -384,7 +384,11 @@
}
public interface FocusPropertiesModifierNode extends androidx.compose.ui.node.DelegatableNode {
- method public void modifyFocusProperties(androidx.compose.ui.focus.FocusProperties focusProperties);
+ method public void applyFocusProperties(androidx.compose.ui.focus.FocusProperties focusProperties);
+ }
+
+ public final class FocusPropertiesModifierNodeKt {
+ method public static void invalidateFocusProperties(androidx.compose.ui.focus.FocusPropertiesModifierNode);
}
@androidx.compose.runtime.Stable public final class FocusRequester {
@@ -2375,8 +2379,11 @@
}
public interface SemanticsModifierNode extends androidx.compose.ui.node.DelegatableNode {
- method public androidx.compose.ui.semantics.SemanticsConfiguration getSemanticsConfiguration();
- property public abstract androidx.compose.ui.semantics.SemanticsConfiguration semanticsConfiguration;
+ method public void applySemantics(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public default boolean getShouldClearDescendantSemantics();
+ method public default boolean getShouldMergeDescendantSemantics();
+ property public default boolean shouldClearDescendantSemantics;
+ property public default boolean shouldMergeDescendantSemantics;
}
public final class SemanticsModifierNodeKt {
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index b296a78..6b29580 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -480,7 +480,11 @@
}
public interface FocusPropertiesModifierNode extends androidx.compose.ui.node.DelegatableNode {
- method public void modifyFocusProperties(androidx.compose.ui.focus.FocusProperties focusProperties);
+ method public void applyFocusProperties(androidx.compose.ui.focus.FocusProperties focusProperties);
+ }
+
+ public final class FocusPropertiesModifierNodeKt {
+ method public static void invalidateFocusProperties(androidx.compose.ui.focus.FocusPropertiesModifierNode);
}
@androidx.compose.runtime.Stable public final class FocusRequester {
@@ -2602,8 +2606,11 @@
}
public interface SemanticsModifierNode extends androidx.compose.ui.node.DelegatableNode {
- method public androidx.compose.ui.semantics.SemanticsConfiguration getSemanticsConfiguration();
- property public abstract androidx.compose.ui.semantics.SemanticsConfiguration semanticsConfiguration;
+ method public void applySemantics(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public default boolean getShouldClearDescendantSemantics();
+ method public default boolean getShouldMergeDescendantSemantics();
+ property public default boolean shouldClearDescendantSemantics;
+ property public default boolean shouldMergeDescendantSemantics;
}
public final class SemanticsModifierNodeKt {
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index 578a0d2..c17196f 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -384,7 +384,11 @@
}
public interface FocusPropertiesModifierNode extends androidx.compose.ui.node.DelegatableNode {
- method public void modifyFocusProperties(androidx.compose.ui.focus.FocusProperties focusProperties);
+ method public void applyFocusProperties(androidx.compose.ui.focus.FocusProperties focusProperties);
+ }
+
+ public final class FocusPropertiesModifierNodeKt {
+ method public static void invalidateFocusProperties(androidx.compose.ui.focus.FocusPropertiesModifierNode);
}
@androidx.compose.runtime.Stable public final class FocusRequester {
@@ -2423,8 +2427,11 @@
}
public interface SemanticsModifierNode extends androidx.compose.ui.node.DelegatableNode {
- method public androidx.compose.ui.semantics.SemanticsConfiguration getSemanticsConfiguration();
- property public abstract androidx.compose.ui.semantics.SemanticsConfiguration semanticsConfiguration;
+ method public void applySemantics(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public default boolean getShouldClearDescendantSemantics();
+ method public default boolean getShouldMergeDescendantSemantics();
+ property public default boolean shouldClearDescendantSemantics;
+ property public default boolean shouldMergeDescendantSemantics;
}
public final class SemanticsModifierNodeKt {
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/accessibility/ComplexAccessibility.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/accessibility/ComplexAccessibility.kt
index e8aed9a..6a22a71 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/accessibility/ComplexAccessibility.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/accessibility/ComplexAccessibility.kt
@@ -21,7 +21,9 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.BottomAppBar
@@ -29,12 +31,14 @@
import androidx.compose.material.FabPosition
import androidx.compose.material.FloatingActionButton
import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Face
import androidx.compose.material.rememberDrawerState
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable
@@ -42,6 +46,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.isTraversalGroup
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.traversalIndex
@@ -350,3 +355,47 @@
}
}
}
+
+@Preview
+@Composable
+fun IconsInScaffoldWithListDemo() {
+ Scaffold(
+ topBar = {
+ Row(
+ horizontalArrangement = Arrangement.SpaceEvenly
+ ) {
+ IconButton(onClick = { }) {
+ Icon(Icons.Default.Face, contentDescription = "Face 1")
+ }
+ // Setting `clearAndSetSemantics` below means that Face 2 will not be sorted nor
+ // will be read by TalkBack. The final traversal order should go from Face 1 to
+ // Face 3 to the LazyColumn content.
+ IconButton(
+ onClick = { },
+ modifier = Modifier.clearAndSetSemantics { }
+ ) {
+ Icon(Icons.Default.Face, contentDescription = "Face 2")
+ }
+ IconButton(onClick = { }) {
+ Icon(Icons.Default.Face, contentDescription = "Face 3")
+ }
+ }
+ },
+ content = { innerPadding ->
+ LazyColumn(
+ contentPadding = innerPadding,
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ val list = (0..75).map { it.toString() }
+ items(count = list.size) {
+ Text(
+ text = list[it],
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp)
+ )
+ }
+ }
+ }
+ )
+}
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/ModifierSamples.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/ModifierSamples.kt
index a61a41e..020c4b9 100644
--- a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/ModifierSamples.kt
+++ b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/ModifierSamples.kt
@@ -51,7 +51,7 @@
import androidx.compose.ui.node.SemanticsModifierNode
import androidx.compose.ui.node.requireLayoutDirection
import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.semantics.SemanticsConfiguration
+import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.unit.IntSize
@@ -155,13 +155,12 @@
gesture.onCancelPointerInput()
}
- override val semanticsConfiguration: SemanticsConfiguration = SemanticsConfiguration()
- .apply {
- onClick {
- gesture.onTap()
- true
- }
+ override fun SemanticsPropertyReceiver.applySemantics() {
+ onClick {
+ gesture.onTap()
+ true
}
+ }
}
}
@@ -184,13 +183,12 @@
}
class TapSemanticsNode(var onTap: () -> Unit) : SemanticsModifierNode, Modifier.Node() {
- override val semanticsConfiguration: SemanticsConfiguration = SemanticsConfiguration()
- .apply {
- onClick {
- onTap()
- true
- }
+ override fun SemanticsPropertyReceiver.applySemantics() {
+ onClick {
+ onTap()
+ true
}
+ }
}
class TapGestureWithClickSemantics(onTap: () -> Unit) : DelegatingNode() {
var onTap: () -> Unit
@@ -250,13 +248,12 @@
}
class TapSemanticsNode(var onTap: () -> Unit) : SemanticsModifierNode, Modifier.Node() {
- override val semanticsConfiguration: SemanticsConfiguration = SemanticsConfiguration()
- .apply {
- onClick {
- onTap()
- true
- }
+ override fun SemanticsPropertyReceiver.applySemantics() {
+ onClick {
+ onTap()
+ true
}
+ }
}
class TapGestureWithClickSemantics(onTap: () -> Unit) : DelegatingNode() {
var onTap: () -> Unit
@@ -340,10 +337,9 @@
@Composable
fun SemanticsModifierNodeSample() {
class HeadingNode : SemanticsModifierNode, Modifier.Node() {
- override val semanticsConfiguration: SemanticsConfiguration = SemanticsConfiguration()
- .apply {
- heading()
- }
+ override fun SemanticsPropertyReceiver.applySemantics() {
+ heading()
+ }
}
val HeadingElement = object : ModifierNodeElement<HeadingNode>() {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
index e2ebcd9..5d49476 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
@@ -79,6 +79,7 @@
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Face
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.rememberDrawerState
import androidx.compose.material.rememberScaffoldState
@@ -141,6 +142,7 @@
import androidx.compose.ui.test.assertValueEquals
import androidx.compose.ui.test.isEnabled
import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
@@ -1261,6 +1263,119 @@
assertThat(contentTraverseBefore).isLessThan(bottomAppBarNode.id)
}
+ @Test
+ fun testSortedAccessibilityNodeInfo_clearSemantics() {
+ val content1 = "Face 1"
+ val content2 = "Face 2"
+ val content3 = "Face 3"
+ val contentText = "Content"
+ container.setContent {
+ Scaffold(
+ topBar = {
+ Row(
+ horizontalArrangement = Arrangement.SpaceEvenly
+ ) {
+ IconButton(onClick = { }) {
+ Icon(Icons.Default.Face, contentDescription = content1)
+ }
+ IconButton(
+ onClick = { },
+ modifier = Modifier.clearAndSetSemantics { }
+ ) {
+ Icon(Icons.Default.Face, contentDescription = content2)
+ }
+ IconButton(onClick = { }) {
+ Icon(Icons.Default.Face, contentDescription = content3)
+ }
+ }
+ },
+ content = { padding -> Text(contentText, modifier = Modifier.padding(padding)) }
+ )
+ }
+ val faceNode1 = rule.onNodeWithContentDescription(content1).fetchSemanticsNode()
+ val faceNode3 = rule.onNodeWithContentDescription(content3).fetchSemanticsNode()
+ val contentNode = rule.onNodeWithText(contentText).fetchSemanticsNode()
+
+ val ANI1 = provider.createAccessibilityNodeInfo(faceNode1.id)
+ val ANI3 = provider.createAccessibilityNodeInfo(faceNode3.id)
+
+ val traverseBefore1 = ANI1?.extras?.getInt(EXTRA_DATA_TEST_TRAVERSALBEFORE_VAL)
+ val traverseBefore3 = ANI3?.extras?.getInt(EXTRA_DATA_TEST_TRAVERSALBEFORE_VAL)
+
+ // On screen we have three faces in a top app bar, and then a content node:
+ //
+ // Face1 Face2 Face3
+ // Content
+ //
+
+ // Since `clearAndSetSemantics` is set on Face2, it should not generate any semantics node.
+ rule.onNodeWithTag(content2).assertDoesNotExist()
+
+ // The traversal order for the elements on screen should then be Face1 -> Face3 -> content.
+ assertEquals(traverseBefore1, faceNode3.id)
+ assertEquals(traverseBefore3, contentNode.id)
+ }
+
+ @Test
+ fun testSortedAccessibilityNodeInfo_zOcclusion() {
+ val parentBox1Tag = "ParentForOverlappedChildren"
+ val childOneTag = "OverlappedChildOne"
+ val childTwoTag = "OverlappedChildTwo"
+ val childThreeTag = "ChildThree"
+
+ container.setContent {
+ Column {
+ Box(Modifier.testTag(parentBox1Tag)) {
+ with(LocalDensity.current) {
+ BasicText(
+ "Child One",
+ Modifier
+ // A child with larger [zIndex] will be drawn on top of all the
+ // children with smaller [zIndex]. So child 1 covers child 2.
+ .zIndex(1f)
+ .testTag(childOneTag)
+ .requiredSize(50.toDp())
+ )
+ BasicText(
+ "Child Two",
+ Modifier
+ .testTag(childTwoTag)
+ .requiredSize(50.toDp())
+ )
+ }
+ }
+ Box {
+ BasicText(
+ "Child Three",
+ Modifier
+ .testTag(childThreeTag)
+ )
+ }
+ }
+ }
+
+ val parentBox1Node = rule.onNodeWithTag(parentBox1Tag).fetchSemanticsNode()
+ val childOneNode = rule.onNodeWithTag(
+ childOneTag, useUnmergedTree = true).fetchSemanticsNode()
+ val childTwoNode = rule.onNodeWithTag(
+ childTwoTag, useUnmergedTree = true).fetchSemanticsNode()
+ val childThreeNode = rule.onNodeWithTag(
+ childThreeTag, useUnmergedTree = true).fetchSemanticsNode()
+
+ val ANI1 = provider.createAccessibilityNodeInfo(childOneNode.id)
+ val traverseBefore1 = ANI1?.extras?.getInt(EXTRA_DATA_TEST_TRAVERSALBEFORE_VAL)
+
+ // Since child 2 is completely covered, it should not generate any ANI. The first box
+ // parent should only have one child (child 1).
+ assertEquals(
+ 1, provider.createAccessibilityNodeInfo(parentBox1Node.id)!!.childCount)
+ assertNull(provider.createAccessibilityNodeInfo(childTwoNode.id))
+
+ // The traversal order for the elements on screen should then be child 1 -> child 3,
+ // completely skipping over child 2.
+ assertEquals(traverseBefore1, childThreeNode.id)
+ }
+
@Composable
fun ScrollColumn(testTag: String) {
var counter = 0
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
index 4e95ffd..337f866 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
@@ -29,15 +29,44 @@
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.runtime.structuralEqualityPolicy
+import androidx.compose.ui.autofill.Autofill
+import androidx.compose.ui.autofill.AutofillTree
+import androidx.compose.ui.focus.FocusDirection
+import androidx.compose.ui.focus.FocusOwner
+import androidx.compose.ui.geometry.MutableRect
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Canvas
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.CompositingStrategy
+import androidx.compose.ui.graphics.Matrix
+import androidx.compose.ui.graphics.RenderEffect
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.toAndroidRect
+import androidx.compose.ui.hapticfeedback.HapticFeedback
+import androidx.compose.ui.input.InputModeManager
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.pointer.PointerIconService
+import androidx.compose.ui.modifier.ModifierLocalManager
+import androidx.compose.ui.node.InternalCoreApi
import androidx.compose.ui.node.LayoutNode
+import androidx.compose.ui.node.LayoutNodeDrawScope
+import androidx.compose.ui.node.OwnedLayer
+import androidx.compose.ui.node.Owner
+import androidx.compose.ui.node.OwnerSnapshotObserver
+import androidx.compose.ui.node.RootForTest
+import androidx.compose.ui.platform.AccessibilityManager
import androidx.compose.ui.platform.AndroidComposeView
import androidx.compose.ui.platform.AndroidComposeViewAccessibilityDelegateCompat
import androidx.compose.ui.platform.AndroidComposeViewAccessibilityDelegateCompat.SemanticsNodeCopy
+import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.SemanticsNodeWithAdjustedBounds
+import androidx.compose.ui.platform.TextToolbar
+import androidx.compose.ui.platform.ViewConfiguration
+import androidx.compose.ui.platform.WindowInfo
import androidx.compose.ui.platform.getAllUncoveredSemanticsNodesToMap
+import androidx.compose.ui.platform.invertTo
import androidx.compose.ui.semantics.CustomAccessibilityAction
import androidx.compose.ui.semantics.EmptySemanticsElement
import androidx.compose.ui.semantics.LiveRegionMode
@@ -78,11 +107,23 @@
import androidx.compose.ui.semantics.text
import androidx.compose.ui.semantics.textSelectionRange
import androidx.compose.ui.semantics.verticalScrollAxisRange
+import androidx.compose.ui.test.InternalTestApi
import androidx.compose.ui.test.TestActivity
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.input.PlatformTextInputPluginRegistry
+import androidx.compose.ui.text.input.TextInputService
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.toOffset
import androidx.core.view.ViewCompat
import androidx.core.view.ViewStructureCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
@@ -106,6 +147,7 @@
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import java.util.concurrent.Executors
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.asCoroutineDispatcher
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
@@ -1593,6 +1635,7 @@
accessibilityDelegate.sendSemanticsPropertyChangeEvents(mapOf(nodeId to newTextNode))
}
+ @OptIn(InternalTestApi::class)
private fun createSemanticsNodeWithProperties(
id: Int,
mergeDescendants: Boolean,
@@ -1602,6 +1645,7 @@
layoutNode.modifier = Modifier.semantics(mergeDescendants) {
properties()
}
+ layoutNode.attach(MockOwner())
return SemanticsNode(layoutNode, true)
}
@@ -1615,6 +1659,7 @@
layoutNode.modifier = Modifier.semantics {
properties()
}
+ layoutNode.attach(MockOwner())
return SemanticsNode(layoutNode, true)
}
@@ -1641,3 +1686,228 @@
return false
}
}
+
+internal class MockOwner(
+ private val position: IntOffset = IntOffset.Zero,
+ override val root: LayoutNode = LayoutNode(),
+ override val coroutineContext: CoroutineContext =
+ Executors.newFixedThreadPool(3).asCoroutineDispatcher()
+) : Owner {
+ val onRequestMeasureParams = mutableListOf<LayoutNode>()
+ val onAttachParams = mutableListOf<LayoutNode>()
+ val onDetachParams = mutableListOf<LayoutNode>()
+ var layoutChangeCount = 0
+
+ override val rootForTest: RootForTest
+ get() = TODO("Not yet implemented")
+ override val hapticFeedBack: HapticFeedback
+ get() = TODO("Not yet implemented")
+ override val inputModeManager: InputModeManager
+ get() = TODO("Not yet implemented")
+ override val clipboardManager: ClipboardManager
+ get() = TODO("Not yet implemented")
+ override val accessibilityManager: AccessibilityManager
+ get() = TODO("Not yet implemented")
+ override val textToolbar: TextToolbar
+ get() = TODO("Not yet implemented")
+
+ @OptIn(ExperimentalComposeUiApi::class)
+ override val autofillTree: AutofillTree
+ get() = TODO("Not yet implemented")
+
+ @OptIn(ExperimentalComposeUiApi::class)
+ override val autofill: Autofill?
+ get() = TODO("Not yet implemented")
+ override val density: Density
+ get() = Density(1f)
+ override val textInputService: TextInputService
+ get() = TODO("Not yet implemented")
+ @OptIn(ExperimentalTextApi::class)
+ override val platformTextInputPluginRegistry: PlatformTextInputPluginRegistry
+ get() = TODO("Not yet implemented")
+ override val pointerIconService: PointerIconService
+ get() = TODO("Not yet implemented")
+ override val focusOwner: FocusOwner
+ get() = TODO("Not yet implemented")
+ override val windowInfo: WindowInfo
+ get() = TODO("Not yet implemented")
+
+ @Deprecated(
+ "fontLoader is deprecated, use fontFamilyResolver",
+ replaceWith = ReplaceWith("fontFamilyResolver")
+ )
+ @Suppress("DEPRECATION")
+ override val fontLoader: Font.ResourceLoader
+ get() = TODO("Not yet implemented")
+ override val fontFamilyResolver: FontFamily.Resolver
+ get() = TODO("Not yet implemented")
+ override val layoutDirection: LayoutDirection
+ get() = LayoutDirection.Ltr
+ @InternalCoreApi
+ override var showLayoutBounds: Boolean = false
+ override val snapshotObserver = OwnerSnapshotObserver { it.invoke() }
+ override val modifierLocalManager: ModifierLocalManager = ModifierLocalManager(this)
+
+ override fun onRequestMeasure(
+ layoutNode: LayoutNode,
+ affectsLookahead: Boolean,
+ forceRequest: Boolean,
+ scheduleMeasureAndLayout: Boolean
+ ) {
+ onRequestMeasureParams += layoutNode
+ if (affectsLookahead) {
+ layoutNode.markLookaheadMeasurePending()
+ }
+ layoutNode.markMeasurePending()
+ }
+
+ override fun onRequestRelayout(
+ layoutNode: LayoutNode,
+ affectsLookahead: Boolean,
+ forceRequest: Boolean
+ ) {
+ if (affectsLookahead) {
+ layoutNode.markLookaheadLayoutPending()
+ }
+ layoutNode.markLayoutPending()
+ }
+
+ override fun requestOnPositionedCallback(layoutNode: LayoutNode) {
+ }
+
+ override fun onAttach(node: LayoutNode) {
+ onAttachParams += node
+ }
+
+ override fun onDetach(node: LayoutNode) {
+ onDetachParams += node
+ }
+
+ override fun calculatePositionInWindow(localPosition: Offset): Offset =
+ localPosition + position.toOffset()
+
+ override fun calculateLocalPosition(positionInWindow: Offset): Offset =
+ positionInWindow - position.toOffset()
+
+ override fun requestFocus(): Boolean = false
+
+ override fun measureAndLayout(sendPointerUpdate: Boolean) {
+ }
+
+ override fun measureAndLayout(layoutNode: LayoutNode, constraints: Constraints) {
+ }
+
+ override fun forceMeasureTheSubtree(layoutNode: LayoutNode, affectsLookahead: Boolean) {
+ }
+
+ override fun registerOnEndApplyChangesListener(listener: () -> Unit) {
+ listener()
+ }
+
+ override fun onEndApplyChanges() {
+ }
+
+ override fun registerOnLayoutCompletedListener(listener: Owner.OnLayoutCompletedListener) {
+ TODO("Not yet implemented")
+ }
+
+ val invalidatedLayers = mutableListOf<OwnedLayer>()
+
+ override fun createLayer(
+ drawBlock: (Canvas) -> Unit,
+ invalidateParentLayer: () -> Unit
+ ): OwnedLayer {
+ val transform = Matrix()
+ val inverseTransform = Matrix()
+ return object : OwnedLayer {
+ override fun updateLayerProperties(
+ scaleX: Float,
+ scaleY: Float,
+ alpha: Float,
+ translationX: Float,
+ translationY: Float,
+ shadowElevation: Float,
+ rotationX: Float,
+ rotationY: Float,
+ rotationZ: Float,
+ cameraDistance: Float,
+ transformOrigin: TransformOrigin,
+ shape: Shape,
+ clip: Boolean,
+ renderEffect: RenderEffect?,
+ ambientShadowColor: Color,
+ spotShadowColor: Color,
+ compositingStrategy: CompositingStrategy,
+ layoutDirection: LayoutDirection,
+ density: Density
+ ) {
+ transform.reset()
+ // This is not expected to be 100% accurate
+ transform.scale(scaleX, scaleY)
+ transform.rotateZ(rotationZ)
+ transform.translate(translationX, translationY)
+ transform.invertTo(inverseTransform)
+ }
+
+ override fun isInLayer(position: Offset) = true
+
+ override fun move(position: IntOffset) {
+ }
+
+ override fun resize(size: IntSize) {
+ }
+
+ override fun drawLayer(canvas: Canvas) {
+ drawBlock(canvas)
+ }
+
+ override fun updateDisplayList() {
+ }
+
+ override fun invalidate() {
+ invalidatedLayers.add(this)
+ }
+
+ override fun destroy() {
+ }
+
+ override fun mapBounds(rect: MutableRect, inverse: Boolean) {
+ }
+
+ override fun reuseLayer(
+ drawBlock: (Canvas) -> Unit,
+ invalidateParentLayer: () -> Unit
+ ) {
+ }
+
+ override fun transform(matrix: Matrix) {
+ matrix.timesAssign(transform)
+ }
+
+ override fun inverseTransform(matrix: Matrix) {
+ matrix.timesAssign(inverseTransform)
+ }
+
+ override fun mapOffset(point: Offset, inverse: Boolean) = point
+ }
+ }
+
+ var semanticsChanged: Boolean = false
+ override fun onSemanticsChange() {
+ semanticsChanged = true
+ }
+
+ override fun onLayoutChange(layoutNode: LayoutNode) {
+ layoutChangeCount++
+ }
+
+ override fun getFocusDirection(keyEvent: KeyEvent): FocusDirection? {
+ TODO("Not yet implemented")
+ }
+
+ override var measureIteration: Long = 0
+ override val viewConfiguration: ViewConfiguration
+ get() = TODO("Not yet implemented")
+
+ override val sharedDrawScope = LayoutNodeDrawScope()
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CombinedFocusModifierNodeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CombinedFocusModifierNodeTest.kt
index 609463e..75515b6 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CombinedFocusModifierNodeTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CombinedFocusModifierNodeTest.kt
@@ -142,7 +142,7 @@
}
/**
- * This test checks that [FocusPropertiesModifierNode.modifyFocusProperties] is called when a
+ * This test checks that [FocusPropertiesModifierNode.applyFocusProperties] is called when a
* property changes.
*/
@Test
@@ -233,7 +233,7 @@
this.focusState = focusState
}
- override fun modifyFocusProperties(focusProperties: FocusProperties) {
+ override fun applyFocusProperties(focusProperties: FocusProperties) {
focusProperties.canFocus = canFocus
}
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilterTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilterTest.kt
index 32fd60c..f761c28 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilterTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilterTest.kt
@@ -4405,38 +4405,3 @@
)
internal typealias PointerEventHandler = (PointerEvent, PointerEventPass, IntSize) -> Unit
-
-private fun PointerEventHandler.invokeOverAllPasses(
- pointerEvent: PointerEvent,
- size: IntSize = IntSize(Int.MAX_VALUE, Int.MAX_VALUE)
-) {
- invokeOverPasses(
- pointerEvent,
- listOf(
- PointerEventPass.Initial,
- PointerEventPass.Main,
- PointerEventPass.Final
- ),
- size = size
- )
-}
-
-private fun PointerEventHandler.invokeOverPasses(
- pointerEvent: PointerEvent,
- vararg pointerEventPasses: PointerEventPass,
- size: IntSize = IntSize(Int.MAX_VALUE, Int.MAX_VALUE)
-) {
- invokeOverPasses(pointerEvent, pointerEventPasses.toList(), size)
-}
-
-private fun PointerEventHandler.invokeOverPasses(
- pointerEvent: PointerEvent,
- pointerEventPasses: List<PointerEventPass>,
- size: IntSize = IntSize(Int.MAX_VALUE, Int.MAX_VALUE)
-) {
- require(pointerEvent.changes.isNotEmpty())
- require(pointerEventPasses.isNotEmpty())
- pointerEventPasses.forEach {
- this.invoke(pointerEvent, it, size)
- }
-}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterCoroutineJobTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterCoroutineJobTest.kt
new file mode 100644
index 0000000..ddf5536
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterCoroutineJobTest.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.compose.ui.input.pointer
+
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.waitForUpOrCancellation
+import androidx.compose.foundation.layout.Box
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.IntSize
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import kotlin.test.assertEquals
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import org.junit.Assert.assertTrue
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class SuspendingPointerInputFilterCoroutineJobTest {
+ @OptIn(ExperimentalTestApi::class)
+ @get:Rule
+ val rule = createComposeRule(Dispatchers.Main)
+
+ @Test
+ @LargeTest
+ fun isPointerInputJobStillActive_cancelPointerEvent_assertsTrue() {
+ val latch = CountDownLatch(1)
+
+ var repeatActualNumber = 0
+ val repeatExpectedNumber = 4
+
+ // To create Pointer Events:
+ val emitter = PointerInputChangeEmitter()
+ val change = emitter.nextChange(Offset(5f, 5f))
+
+ // Used to manually trigger a PointerEvent created from our PointerInputChange.
+ val suspendingPointerInputModifierNode = SuspendingPointerInputModifierNode {
+ coroutineScope {
+ awaitEachGesture {
+ // First down event (triggered)
+ awaitFirstDown()
+ // Up event won't ever come since we never trigger the event, times out as
+ // a cancellation
+ waitForUpOrCancellation()
+
+ // By running this code after down/up, we are making sure the block of code
+ // for handling pointer input events is still active despite the cancel pointer
+ // input being called later.
+ launch(start = CoroutineStart.UNDISPATCHED) {
+ repeat(repeatExpectedNumber) {
+ repeatActualNumber++
+ delay(100)
+ }
+ latch.countDown()
+ }
+ }
+ }
+ }
+
+ rule.setContent {
+ Box(
+ modifier = elementFor(
+ key1 = Unit,
+ instance = suspendingPointerInputModifierNode as Modifier.Node
+ )
+ )
+ }
+
+ rule.runOnIdle {
+ suspendingPointerInputModifierNode.onPointerEvent(
+ change.toPointerEvent(),
+ PointerEventPass.Main,
+ IntSize(10, 10)
+ )
+ }
+
+ suspendingPointerInputModifierNode.onCancelPointerInput()
+
+ val resultOfLatch = latch.await(3000, TimeUnit.MILLISECONDS)
+ assertTrue("Waiting for coroutine's tasks to finish", resultOfLatch)
+ assertEquals(repeatExpectedNumber, repeatActualNumber)
+ }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt
index 1d3593d..c521919 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt
@@ -24,10 +24,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.node.DelegatingNode
-import androidx.compose.ui.node.ModifierNodeElement
-import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.platform.ValueElement
-import androidx.compose.ui.platform.debugInspectorInfo
import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.elementFor
@@ -273,7 +270,7 @@
)
}
- // Triggers cancel event
+ // Manually cancels the current pointer input event.
suspendingPointerInputModifierNode.onCancelPointerInput()
}
@@ -305,15 +302,16 @@
var currentEventAtEnd: PointerEvent? = null
val results = Channel<PointerEvent>(Channel.UNLIMITED)
- // Used to manually trigger a PointerEvent(s) created from our PointerInputChange(s).
+ // Used to manually trigger/test PointerEvents and other functionality.
val suspendingPointerInputModifierNode = SuspendingPointerInputModifierNode {
awaitPointerEventScope {
try {
// NOTE: This will never trigger 3 times. There are only two events
- // triggered followed by a onCancelPointerInput() call which doesn't trigger
- // an event because the previous event has down (press) set to false, so we
- // will always get an exception thrown with the last repeat's timeout
- // (we expect this).
+ // triggered (a down [press] event followed by an event with down [press] set to
+ // false). The resetPointerInputHandler() is called after that, but it won't
+ // trigger a cancel event if the previous event has the down [press] set to
+ // false. In this case, it does, so there are only ever two events, so the
+ // withTimeout() will trigger the timeout (expected).
repeat(3) {
withTimeout(200) {
results.trySend(awaitPointerEvent())
@@ -334,7 +332,6 @@
)
)
}
-
val bounds = IntSize(50, 50)
val emitter1 = PointerInputChangeEmitter(0)
val emitter2 = PointerInputChangeEmitter(1)
@@ -378,9 +375,9 @@
)
}
- // Manually triggers cancel event.
- // Note: This will not trigger an event in the customPointerInput block because the
- // previous events don't have any pressed pointers.
+ // Manually cancels the current pointer input event.
+ // Note: This will not trigger an event in the customPointerInput block because of the
+ // reasons listed above (previous event has down (press) set to false).
suspendingPointerInputModifierNode.onCancelPointerInput()
}
@@ -411,35 +408,48 @@
@Test
@MediumTest
- fun testCancelledHandlerBlock() {
+ fun testCancelPointerInput() {
+ val firstEventCheckpointInfo =
+ Pair(3, "First pointer event triggered to create Job.")
+ val cancelEventCheckpointInfo =
+ Pair(5, "Cancel pointer event triggered (shouldn't cancel Job).")
+ val invalidEventCheckpointNumber =
+ Pair(-1, "Should never execute.")
+
val counter = TestCounter()
- // Used to manually trigger a PointerEvent(s) created from our PointerInputChange(s).
+ // Used to manually trigger/test PointerEvents and other functionality.
val suspendingPointerInputModifierNode = SuspendingPointerInputModifierNode {
try {
awaitPointerEventScope {
try {
- counter.expect(3, "about to call awaitPointerEvent")
+ counter.expect(2, "Before awaitPointerEvent() call.")
- // With only one event triggered, this will stay stuck in the repeat
- // block until the Job is cancelled via
- // SuspendPointerInputModifierNode.resetHandling()
- repeat(2) {
+ // With only two events triggered (press and cancel), this will stay stuck
+ // in the repeat block until it is torn down, that is, until the
+ // test is over.
+ repeat(3) { repeatCount ->
awaitPointerEvent()
- counter.expect(
- 4,
- "One and only pointer event triggered to create Job."
- )
+ val checkpointInfo = when (repeatCount) {
+ 0 -> { firstEventCheckpointInfo }
+ 1 -> { cancelEventCheckpointInfo }
+ else -> {
+ fail("Should never be three events.")
+ invalidEventCheckpointNumber
+ }
+ }
+ counter.expect(checkpointInfo.first, checkpointInfo.second)
}
-
- fail("awaitPointerEvent returned; should have thrown for cancel")
+ fail("awaitPointerEvent() run 3+ times in repeat() block, should only " +
+ "have run twice (one event, one cancel).")
} finally {
- counter.expect(6, "inner finally block running")
+ counter.expect(7, "Inner finally block runs after " +
+ "teardown.")
}
}
} finally {
- counter.expect(7, "outer finally block running; inner " +
- "finally should have run")
+ counter.expect(8, "Outer finally block runs; inner finally " +
+ "block should have already run.")
}
}
@@ -457,16 +467,13 @@
val singleEventBounds = IntSize(20, 20)
rule.runOnIdle {
+ // Because the pointer input handler is triggered lazily in
+ // SuspendPointerInputModifierNode, it will not be triggered until the first event
+ // comes in, so this will be the first counter checkpoint.
counter.expect(
1,
- "Job to handle pointer input not created yet; awaitPointerEvent should " +
- "be suspended"
- )
-
- counter.expect(
- 2,
- "Trigger pointer input event to create Job for handing handle pointer" +
- " input (done lazily in SuspendPointerInputModifierNode)."
+ "Trigger pointer input handler through first pointer input event " +
+ "(handler triggered lazily)."
)
suspendingPointerInputModifierNode.onPointerEvent(
@@ -475,12 +482,96 @@
singleEventBounds
)
- counter.expect(5, "before cancelling handler; awaitPointerEvent " +
+ counter.expect(4, "Before onCancelPointerInput() handler; awaitPointerEvent " +
"should be suspended")
- // Cancels Job that manages pointer input events in SuspendPointerInputModifierNode.
+ // Manually cancels the current pointer input event.
+ suspendingPointerInputModifierNode.onCancelPointerInput()
+ counter.expect(6, "After onCancelPointerInput(), end of test, " +
+ " start teardown.")
+ }
+ }
+
+ @Test
+ @MediumTest
+ fun testResetHandlerBlock() {
+ val counter = TestCounter()
+
+ // Used to manually trigger/test PointerEvents and other functionality.
+ val suspendingPointerInputModifierNode = SuspendingPointerInputModifierNode {
+ try {
+ awaitPointerEventScope {
+ try {
+ counter.expect(2, "Before awaitPointerEvent() call.")
+
+ // With only one event triggered (press) to kick start the handler, this
+ // will stay stuck in the repeat block until it is torn down, that is,
+ // until the test is over.
+ repeat(2) { repeatCount ->
+ awaitPointerEvent()
+ when (repeatCount) {
+ 0 -> {
+ counter.expect(
+ 3,
+ "First/only pointer event triggered."
+ )
+ }
+ else -> {
+ fail("Should never be two or more events.")
+ counter.expect(-1, "Should never execute.")
+ }
+ }
+ }
+ fail("awaitPointerEvent repeated twice; should have only happened once " +
+ "and stayed suspended in repeat() waiting for a second event (that " +
+ "should never arrive).")
+ } finally {
+ fail("inner finally shouldn't call during teardown since coroutine job " +
+ "was cancelled with resetPointerInputHandler().")
+ }
+ }
+ } finally {
+ counter.expect(5, "outer finally block runs after " +
+ "resetPointerInputHandler().")
+ }
+ }
+
+ rule.setContent {
+ Box(
+ modifier = elementFor(
+ key1 = Unit,
+ instance = suspendingPointerInputModifierNode as Modifier.Node
+ )
+ )
+ }
+
+ val emitter = PointerInputChangeEmitter()
+ val singleEvent = emitter.nextChange(Offset(5f, 5f))
+ val singleEventBounds = IntSize(20, 20)
+
+ rule.runOnIdle {
+ // Because the pointer input handler is triggered lazily in
+ // SuspendPointerInputModifierNode, it will not be triggered until the first event
+ // comes in, so this will be the first counter checkpoint.
+ counter.expect(
+ 1,
+ "Trigger pointer input handler through first pointer input event " +
+ "(handler triggered lazily)."
+ )
+
+ suspendingPointerInputModifierNode.onPointerEvent(
+ singleEvent.toPointerEvent(),
+ PointerEventPass.Main,
+ singleEventBounds
+ )
+
+ counter.expect(4, "before resetPointerInputHandler(), handler should" +
+ "be suspended waiting for a second event (that never comes).")
+
+ // Cancels the pointer input handler in SuspendPointerInputModifierNode (and thus the
+ // Coroutine Job associated with it).
suspendingPointerInputModifierNode.resetPointerInputHandler()
- counter.expect(8, "after cancelling; finally blocks should have run")
+ counter.expect(6, "after resetPointerInputHandler(), end of test.")
}
}
@@ -774,72 +865,3 @@
assertThat(events).hasSize(2)
}
}
-
-private fun PointerInputChange.toPointerEvent() = PointerEvent(listOf(this))
-
-private val PointerEvent.firstChange get() = changes.first()
-
-private class PointerInputChangeEmitter(id: Int = 0) {
- val pointerId = PointerId(id.toLong())
- var previousTime = 0L
- var previousPosition = Offset.Zero
- var previousPressed = false
-
- fun nextChange(
- position: Offset = Offset.Zero,
- down: Boolean = true,
- time: Long = 0
- ): PointerInputChange {
- return PointerInputChange(
- id = pointerId,
- time,
- position,
- down,
- previousTime,
- previousPosition,
- previousPressed,
- isInitiallyConsumed = false
- ).also {
- previousTime = time
- previousPosition = position
- previousPressed = down
- }
- }
-}
-
-private class TestCounter {
- private var count = 0
-
- fun expect(checkpoint: Int, message: String = "(no message)") {
- val expected = count + 1
- if (checkpoint != expected) {
- fail("out of order event $checkpoint, expected $expected, $message")
- }
- count = expected
- }
-}
-
-private fun elementFor(
- key1: Any? = null,
- instance: Modifier.Node
-) = object : ModifierNodeElement<Modifier.Node>() {
- override fun InspectorInfo.inspectableProperties() {
- debugInspectorInfo {
- name = "pointerInput"
- properties["key1"] = key1
- properties["instance"] = instance
- }
- }
-
- override fun create() = instance
- override fun update(node: Modifier.Node) {}
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (other !is SuspendPointerInputElement) return false
- if (key1 != other.key1) return false
- return true
- }
- override fun hashCode(): Int {
- return key1?.hashCode() ?: 0
- }
-}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/TestUtils.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/TestUtils.kt
index af27c0d..fb92e8f 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/TestUtils.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/TestUtils.kt
@@ -28,13 +28,17 @@
import androidx.compose.ui.composed
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.NodeCoordinator
import androidx.compose.ui.node.PointerInputModifierNode
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.debugInspectorInfo
import androidx.compose.ui.unit.IntSize
import com.google.common.truth.FailureMetadata
import com.google.common.truth.Subject
import com.google.common.truth.Subject.Factory
import com.google.common.truth.Truth
+import org.junit.Assert
@OptIn(ExperimentalComposeUiApi::class)
internal fun PointerInputEventData(
@@ -555,3 +559,73 @@
type = this.type,
scrollDelta = this.scrollDelta
)
+
+// SuspendingPointerInputFilter test utilities
+internal fun PointerInputChange.toPointerEvent() = PointerEvent(listOf(this))
+
+internal val PointerEvent.firstChange get() = changes.first()
+
+internal class PointerInputChangeEmitter(id: Int = 0) {
+ val pointerId = PointerId(id.toLong())
+ var previousTime = 0L
+ var previousPosition = Offset.Zero
+ var previousPressed = false
+
+ fun nextChange(
+ position: Offset = Offset.Zero,
+ down: Boolean = true,
+ time: Long = 0
+ ): PointerInputChange {
+ return PointerInputChange(
+ id = pointerId,
+ time,
+ position,
+ down,
+ previousTime,
+ previousPosition,
+ previousPressed,
+ isInitiallyConsumed = false
+ ).also {
+ previousTime = time
+ previousPosition = position
+ previousPressed = down
+ }
+ }
+}
+
+internal class TestCounter {
+ private var count = 0
+
+ fun expect(checkpoint: Int, message: String = "(no message)") {
+ val expected = count + 1
+ if (checkpoint != expected) {
+ Assert.fail("out of order event $checkpoint, expected $expected, $message")
+ }
+ count = expected
+ }
+}
+
+internal fun elementFor(
+ key1: Any? = null,
+ instance: Modifier.Node
+) = object : ModifierNodeElement<Modifier.Node>() {
+ override fun InspectorInfo.inspectableProperties() {
+ debugInspectorInfo {
+ name = "pointerInput"
+ properties["key1"] = key1
+ properties["instance"] = instance
+ }
+ }
+
+ override fun create() = instance
+ override fun update(node: Modifier.Node) {}
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is SuspendPointerInputElement) return false
+ if (key1 != other.key1) return false
+ return true
+ }
+ override fun hashCode(): Int {
+ return key1?.hashCode() ?: 0
+ }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
index 5731018..2c1d8dd 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
@@ -106,22 +106,6 @@
}
@Test
- fun valueSemanticsAreEqual() {
- assertEquals(
- Modifier.semantics {
- text = AnnotatedString("text")
- contentDescription = "foo"
- popup()
- },
- Modifier.semantics {
- text = AnnotatedString("text")
- contentDescription = "foo"
- popup()
- }
- )
- }
-
- @Test
fun isTraversalGroupProperty() {
rule.setContent {
Surface(
@@ -1083,10 +1067,9 @@
properties: SemanticsPropertyReceiver.() -> Unit
): CoreSemanticsModifierNode {
return CoreSemanticsModifierNode(
- SemanticsConfiguration().apply {
- isMergingSemanticsOfDescendants = mergeDescendants
- properties()
- }
+ mergeDescendants = mergeDescendants,
+ isClearingSemantics = false,
+ properties = properties,
)
}
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 497be6e..da63070 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
@@ -911,13 +911,20 @@
}
}
- private fun convertMeasureSpec(measureSpec: Int): Pair<Int, Int> {
+ @Suppress("NOTHING_TO_INLINE")
+ private inline operator fun ULong.component1() = (this shr 32).toInt()
+ @Suppress("NOTHING_TO_INLINE")
+ private inline operator fun ULong.component2() = (this and 0xFFFFFFFFUL).toInt()
+
+ private fun pack(a: Int, b: Int) = (a.toULong() shl 32 or b.toULong())
+
+ private fun convertMeasureSpec(measureSpec: Int): ULong {
val mode = MeasureSpec.getMode(measureSpec)
val size = MeasureSpec.getSize(measureSpec)
return when (mode) {
- MeasureSpec.EXACTLY -> size to size
- MeasureSpec.UNSPECIFIED -> 0 to Constraints.Infinity
- MeasureSpec.AT_MOST -> 0 to size
+ MeasureSpec.EXACTLY -> pack(size, size)
+ MeasureSpec.UNSPECIFIED -> pack(0, Constraints.Infinity)
+ MeasureSpec.AT_MOST -> pack(0, size)
else -> throw IllegalStateException()
}
}
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 909c4a5..3baff06 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
@@ -74,7 +74,6 @@
import androidx.compose.ui.semantics.SemanticsOwner
import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.semantics.SemanticsPropertiesAndroid
-import androidx.compose.ui.semantics.collapsedSemantics
import androidx.compose.ui.semantics.getOrNull
import androidx.compose.ui.state.ToggleableState
import androidx.compose.ui.text.AnnotatedString
@@ -694,9 +693,9 @@
fun depthFirstSearch(currNode: SemanticsNode) {
// We only want to add children that are either traversalGroups or are
- // screen reader focusable.
- if (currNode.isTraversalGroup == true ||
- isScreenReaderFocusable(currNode)) {
+ // screen reader focusable. The child must also be in the current pruned semantics tree.
+ if ((currNode.isTraversalGroup == true || isScreenReaderFocusable(currNode)) &&
+ currNode.id in currentSemanticsNodes.keys) {
geometryList.add(currNode)
}
if (currNode.isTraversalGroup == true) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusProperties.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusProperties.kt
index 2cddc19..4aa9f83 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusProperties.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusProperties.kt
@@ -200,7 +200,7 @@
var focusPropertiesScope: FocusProperties.() -> Unit,
) : FocusPropertiesModifierNode, Modifier.Node() {
- override fun modifyFocusProperties(focusProperties: FocusProperties) {
+ override fun applyFocusProperties(focusProperties: FocusProperties) {
focusProperties.apply(focusPropertiesScope)
}
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusPropertiesModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusPropertiesModifierNode.kt
index bac0920..20b61f1 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusPropertiesModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusPropertiesModifierNode.kt
@@ -30,9 +30,9 @@
* [FocusPropertiesModifierNode]s, properties set by a parent higher up in the hierarchy
* overwrite properties set by those that are lower in the hierarchy.
*/
- fun modifyFocusProperties(focusProperties: FocusProperties)
+ fun applyFocusProperties(focusProperties: FocusProperties)
}
-internal fun FocusPropertiesModifierNode.invalidateFocusProperties() {
+fun FocusPropertiesModifierNode.invalidateFocusProperties() {
requireOwner().focusOwner.scheduleInvalidation(this)
}
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt
index cfa7499..2d79ccf 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt
@@ -80,13 +80,13 @@
/**
* Visits parent [FocusPropertiesModifierNode]s and runs
- * [FocusPropertiesModifierNode.modifyFocusProperties] on each parent.
+ * [FocusPropertiesModifierNode.applyFocusProperties] on each parent.
* This effectively collects an aggregated focus state.
*/
internal fun fetchFocusProperties(): FocusProperties {
val properties = FocusPropertiesImpl()
visitSelfAndAncestors(Nodes.FocusProperties, untilType = Nodes.FocusTarget) {
- it.modifyFocusProperties(properties)
+ it.applyFocusProperties(properties)
}
return properties
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/Vector.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/Vector.kt
index 7e68ef8..0e155ef 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/Vector.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/Vector.kt
@@ -19,7 +19,6 @@
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
-import androidx.compose.ui.geometry.Size
import androidx.compose.ui.geometry.Size.Companion.Unspecified
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
@@ -49,12 +48,6 @@
val EmptyPath = emptyList<PathNode>()
-inline fun PathData(block: PathBuilder.() -> Unit): List<PathNode> =
- with(PathBuilder()) {
- block()
- getNodes()
- }
-
const val DefaultPathName = ""
const val DefaultStrokeLineWidth = 0.0f
const val DefaultStrokeLineMiter = 4.0f
@@ -68,15 +61,18 @@
val DefaultTintColor = Color.Transparent
val DefaultFillType = PathFillType.NonZero
-fun addPathNodes(pathStr: String?): List<PathNode> =
- if (pathStr == null) {
- EmptyPath
- } else {
- PathParser().parsePathString(pathStr).toNodes()
- }
+inline fun PathData(block: PathBuilder.() -> Unit) = with(PathBuilder()) {
+ block()
+ getNodes()
+}
+
+fun addPathNodes(pathStr: String?) = if (pathStr == null) {
+ EmptyPath
+} else {
+ PathParser().parsePathString(pathStr).toNodes()
+}
sealed class VNode {
-
/**
* Callback invoked whenever the node in the vector tree is modified in a way that would
* change the output of the Vector
@@ -91,7 +87,6 @@
}
internal class VectorComponent : VNode() {
-
val root = GroupComponent().apply {
pivotX = 0.0f
pivotY = 0.0f
@@ -111,7 +106,7 @@
invalidateCallback.invoke()
}
- private var isDirty: Boolean = true
+ private var isDirty = true
private val cacheDrawScope = DrawCache()
@@ -119,7 +114,7 @@
internal var intrinsicColorFilter: ColorFilter? by mutableStateOf(null)
- var viewportWidth: Float = 0f
+ var viewportWidth = 0f
set(value) {
if (field != value) {
field = value
@@ -127,7 +122,7 @@
}
}
- var viewportHeight: Float = 0f
+ var viewportHeight = 0f
set(value) {
if (field != value) {
field = value
@@ -135,7 +130,7 @@
}
}
- private var previousDrawSize: Size = Unspecified
+ private var previousDrawSize = Unspecified
/**
* Cached lambda used to avoid allocating the lambda on each draw invocation
@@ -145,11 +140,7 @@
}
fun DrawScope.draw(alpha: Float, colorFilter: ColorFilter?) {
- val targetColorFilter = if (colorFilter != null) {
- colorFilter
- } else {
- intrinsicColorFilter
- }
+ val targetColorFilter = colorFilter ?: intrinsicColorFilter
// If the content of the vector has changed, or we are drawing a different size
// update the cached image to ensure we are scaling the vector appropriately
if (isDirty || previousDrawSize != size) {
@@ -182,8 +173,7 @@
}
internal class PathComponent : VNode() {
-
- var name: String = DefaultPathName
+ var name = DefaultPathName
set(value) {
field = value
invalidate()
@@ -195,33 +185,33 @@
invalidate()
}
- var fillAlpha: Float = 1.0f
+ var fillAlpha = 1.0f
set(value) {
field = value
invalidate()
}
- var pathData: List<PathNode> = EmptyPath
+ var pathData = EmptyPath
set(value) {
field = value
isPathDirty = true
invalidate()
}
- var pathFillType: PathFillType = DefaultFillType
+ var pathFillType = DefaultFillType
set(value) {
field = value
renderPath.fillType = value
invalidate()
}
- var strokeAlpha: Float = 1.0f
+ var strokeAlpha = 1.0f
set(value) {
field = value
invalidate()
}
- var strokeLineWidth: Float = DefaultStrokeLineWidth
+ var strokeLineWidth = DefaultStrokeLineWidth
set(value) {
field = value
invalidate()
@@ -233,28 +223,28 @@
invalidate()
}
- var strokeLineCap: StrokeCap = DefaultStrokeLineCap
+ var strokeLineCap = DefaultStrokeLineCap
set(value) {
field = value
isStrokeDirty = true
invalidate()
}
- var strokeLineJoin: StrokeJoin = DefaultStrokeLineJoin
+ var strokeLineJoin = DefaultStrokeLineJoin
set(value) {
field = value
isStrokeDirty = true
invalidate()
}
- var strokeLineMiter: Float = DefaultStrokeLineMiter
+ var strokeLineMiter = DefaultStrokeLineMiter
set(value) {
field = value
isStrokeDirty = true
invalidate()
}
- var trimPathStart: Float = DefaultTrimPathStart
+ var trimPathStart = DefaultTrimPathStart
set(value) {
if (field != value) {
field = value
@@ -263,7 +253,7 @@
}
}
- var trimPathEnd: Float = DefaultTrimPathEnd
+ var trimPathEnd = DefaultTrimPathEnd
set(value) {
if (field != value) {
field = value
@@ -272,7 +262,7 @@
}
}
- var trimPathOffset: Float = DefaultTrimPathOffset
+ var trimPathOffset = DefaultTrimPathOffset
set(value) {
if (field != value) {
field = value
@@ -283,30 +273,34 @@
private var isPathDirty = true
private var isStrokeDirty = true
- private var isTrimPathDirty = true
+ private var isTrimPathDirty = false
private var strokeStyle: Stroke? = null
private val path = Path()
-
- private val renderPath = Path()
+ private var renderPath = path
private val pathMeasure: PathMeasure by lazy(LazyThreadSafetyMode.NONE) { PathMeasure() }
- private val parser = PathParser()
-
private fun updatePath() {
- parser.clear()
- path.reset()
- parser.addPathNodes(pathData).toPath(path)
+ // The call below resets the path
+ pathData.toPath(path)
updateRenderPath()
}
private fun updateRenderPath() {
- renderPath.reset()
if (trimPathStart == DefaultTrimPathStart && trimPathEnd == DefaultTrimPathEnd) {
- renderPath.addPath(path)
+ renderPath = path
} else {
+ if (renderPath == path) {
+ renderPath = Path()
+ } else {
+ // Rewind unsets the fill type so reset it here
+ val fillType = renderPath.fillType
+ renderPath.rewind()
+ renderPath.fillType = fillType
+ }
+
pathMeasure.setPath(path, false)
val length = pathMeasure.length
val start = ((trimPathStart + trimPathOffset) % 1f) * length
@@ -342,18 +336,15 @@
}
}
- override fun toString(): String {
- return path.toString()
- }
+ override fun toString() = path.toString()
}
internal class GroupComponent : VNode() {
-
private var groupMatrix: Matrix? = null
private val children = mutableListOf<VNode>()
- var clipPathData: List<PathNode> = EmptyPath
+ var clipPathData = EmptyPath
set(value) {
field = value
isClipPathDirty = true
@@ -366,7 +357,6 @@
private var isClipPathDirty = true
private var clipPath: Path? = null
- private var parser: PathParser? = null
override var invalidateListener: (() -> Unit)? = null
set(value) {
@@ -378,83 +368,77 @@
private fun updateClipPath() {
if (willClipPath) {
- var targetParser = parser
- if (targetParser == null) {
- targetParser = PathParser()
- parser = targetParser
- } else {
- targetParser.clear()
- }
-
var targetClip = clipPath
if (targetClip == null) {
targetClip = Path()
clipPath = targetClip
- } else {
- targetClip.reset()
}
- targetParser.addPathNodes(clipPathData).toPath(targetClip)
+ // toPath() will reset the path we send
+ clipPathData.toPath(targetClip)
}
}
// If the name changes we should re-draw as individual nodes could
// be modified based off of this name parameter.
- var name: String = DefaultGroupName
+ var name = DefaultGroupName
set(value) {
field = value
invalidate()
}
- var rotation: Float = DefaultRotation
+ var rotation = DefaultRotation
set(value) {
field = value
isMatrixDirty = true
invalidate()
}
- var pivotX: Float = DefaultPivotX
+ var pivotX = DefaultPivotX
set(value) {
field = value
isMatrixDirty = true
invalidate()
}
- var pivotY: Float = DefaultPivotY
+ var pivotY = DefaultPivotY
set(value) {
field = value
isMatrixDirty = true
invalidate()
}
- var scaleX: Float = DefaultScaleX
+ var scaleX = DefaultScaleX
set(value) {
field = value
isMatrixDirty = true
invalidate()
}
- var scaleY: Float = DefaultScaleY
+ var scaleY = DefaultScaleY
set(value) {
field = value
isMatrixDirty = true
invalidate()
}
- var translationX: Float = DefaultTranslationX
+ var translationX = DefaultTranslationX
set(value) {
field = value
isMatrixDirty = true
invalidate()
}
- var translationY: Float = DefaultTranslationY
+ var translationY = DefaultTranslationY
set(value) {
field = value
isMatrixDirty = true
invalidate()
}
+ val numChildren: Int
+ get() = children.size
+
private var isMatrixDirty = true
private fun updateMatrix() {
@@ -541,9 +525,6 @@
}
}
- val numChildren: Int
- get() = children.size
-
override fun toString(): String {
val sb = StringBuilder().append("VGroup: ").append(name)
children.fastForEach { node ->
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/HitPathTracker.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/HitPathTracker.kt
index 9a7ddac..abd9b6a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/HitPathTracker.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/HitPathTracker.kt
@@ -376,12 +376,21 @@
@OptIn(ExperimentalComposeUiApi::class)
for ((key, change) in changes) {
- // Filter for changes that are associated with pointer ids that are relevant to this
- // node
- if (key in pointerIds) {
+ val keyValue = key.value
+
+ // Using for (key in pointerIds) causes key to be boxed and create allocations
+ var keyInPointerIds = false
+ for (i in 0..pointerIds.lastIndex) {
+ if (pointerIds[i].value == keyValue) {
+ keyInPointerIds = true
+ break
+ }
+ }
+
+ if (keyInPointerIds) {
// And translate their position relative to the parent coordinates, to give us a
// change local to the PointerInputFilter's coordinates
- val historical = mutableListOf<HistoricalChange>()
+ val historical = ArrayList<HistoricalChange>(change.historical.size)
change.historical.fastForEach {
historical.add(
HistoricalChange(
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt
index a3d34a1..96c06cc 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt
@@ -602,9 +602,6 @@
dispatchPointerEvent(cancelEvent, PointerEventPass.Final)
lastPointerEvent = null
-
- // Cancels existing coroutine (Job) handling events.
- resetPointerInputHandler()
}
override suspend fun <R> awaitPointerEventScope(
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/BackwardsCompatNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/BackwardsCompatNode.kt
index 774dd70..533ae5f 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/BackwardsCompatNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/BackwardsCompatNode.kt
@@ -59,6 +59,7 @@
import androidx.compose.ui.modifier.modifierLocalMapOf
import androidx.compose.ui.semantics.SemanticsConfiguration
import androidx.compose.ui.semantics.SemanticsModifier
+import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
@@ -352,8 +353,11 @@
}
}
- override val semanticsConfiguration: SemanticsConfiguration
- get() = (element as SemanticsModifier).semanticsConfiguration
+ override fun SemanticsPropertyReceiver.applySemantics() {
+ val config = (element as SemanticsModifier).semanticsConfiguration
+ val toMergeInto = (this as SemanticsConfiguration)
+ toMergeInto.collapsePeer(config)
+ }
override fun onPointerEvent(
pointerEvent: PointerEvent,
@@ -415,7 +419,7 @@
focusEventModifier.onFocusEvent(focusState)
}
- override fun modifyFocusProperties(focusProperties: FocusProperties) {
+ override fun applyFocusProperties(focusProperties: FocusProperties) {
val focusOrderModifier = element
check(focusOrderModifier is FocusOrderModifier)
focusProperties.apply(FocusOrderModifierToProperties(focusOrderModifier))
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 e42df1f..aa215e9 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
@@ -52,6 +52,7 @@
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.platform.simpleIdentityToString
+import androidx.compose.ui.semantics.SemanticsConfiguration
import androidx.compose.ui.semantics.generateSemanticsId
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
@@ -65,6 +66,8 @@
*/
private const val DebugChanges = false
+private val DefaultDensity = Density(1f)
+
/**
* An element in the layout hierarchy, built with compose UI.
*/
@@ -392,6 +395,37 @@
invalidateMeasurements()
}
+ private var _collapsedSemantics: SemanticsConfiguration? = null
+ internal fun invalidateSemantics() {
+ _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() {
+ 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].
@@ -419,7 +453,7 @@
this.owner = owner
this.depth = (parent?.depth ?: -1) + 1
if (nodes.has(Nodes.Semantics)) {
- owner.onSemanticsChange()
+ invalidateSemantics()
}
owner.onAttach(this)
@@ -469,7 +503,7 @@
onDetach?.invoke(owner)
if (nodes.has(Nodes.Semantics)) {
- owner.onSemanticsChange()
+ invalidateSemantics()
}
nodes.detach()
owner.onDetach(this)
@@ -595,7 +629,7 @@
/**
* The screen density to be used by this layout.
*/
- override var density: Density = Density(1f)
+ override var density: Density = DefaultDensity
set(value) {
if (field != value) {
field = value
@@ -1139,10 +1173,8 @@
fun invalidateSubtree(isRootOfInvalidation: Boolean = true) {
if (isRootOfInvalidation) {
parent?.invalidateLayer()
- // Invalidate semantics. We can do this once because there isn't a node-by-node
- // invalidation mechanism.
- requireOwner().onSemanticsChange()
}
+ invalidateSemantics()
requestRemeasure()
nodes.headToTail(Nodes.Layout) {
it.requireCoordinator(Nodes.Layout).layer?.invalidate()
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifierNodeElement.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifierNodeElement.kt
index 0bbb386..7cf6f2d 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifierNodeElement.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifierNodeElement.kt
@@ -89,12 +89,16 @@
tryPopulateReflectively(this@ModifierNodeElement)
}
- // Require hashCode() to be implemented. Using a data class is sufficient. Singletons and
- // modifiers with no parameters may implement this function by returning an arbitrary constant.
+ /**
+ * Require hashCode() to be implemented. Using a data class is sufficient. Singletons and
+ * modifiers with no parameters may implement this function by returning an arbitrary constant.
+ */
abstract override fun hashCode(): Int
- // Require equals() to be implemented. Using a data class is sufficient. Singletons may
- // implement this function with referential equality (`this === other`). Modifiers with no
- // inputs may implement this function by checking the type of the other object.
+ /**
+ * Require equals() to be implemented. Using a data class is sufficient. Singletons may
+ * implement this function with referential equality (`this === other`). Modifiers with no
+ * inputs may implement this function by checking the type of the other object.
+ */
abstract override fun equals(other: Any?): Boolean
}
\ No newline at end of file
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 2b2847f..9043a07 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
@@ -743,7 +743,8 @@
private fun Modifier.fillVector(
result: MutableVector<Modifier.Element>
): MutableVector<Modifier.Element> {
- val stack = MutableVector<Modifier>(result.size).also { it.add(this) }
+ val capacity = result.size.coerceAtLeast(16)
+ val stack = MutableVector<Modifier>(capacity).also { it.add(this) }
while (stack.isNotEmpty()) {
when (val next = stack.removeAt(stack.size - 1)) {
is CombinedModifier -> {
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 b0abc9f..6519bf8 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
@@ -39,7 +39,6 @@
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.layout.findRootCoordinates
import androidx.compose.ui.layout.positionInRoot
-import androidx.compose.ui.semantics.collapsedSemantics
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt
index 813f367..9aade67 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt
@@ -310,7 +310,7 @@
*/
private fun FocusPropertiesModifierNode.specifiesCanFocusProperty(): Boolean {
CanFocusChecker.reset()
- modifyFocusProperties(CanFocusChecker)
+ applyFocusProperties(CanFocusChecker)
return CanFocusChecker.isCanFocusSet()
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OwnerSnapshotObserver.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OwnerSnapshotObserver.kt
index 22391a49..689bc64 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OwnerSnapshotObserver.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OwnerSnapshotObserver.kt
@@ -39,6 +39,12 @@
}
}
+ private val onCommitAffectingSemantics: (LayoutNode) -> Unit = { layoutNode ->
+ if (layoutNode.isValidOwnerScope) {
+ layoutNode.invalidateSemantics()
+ }
+ }
+
private val onCommitAffectingLayout: (LayoutNode) -> Unit = { layoutNode ->
if (layoutNode.isValidOwnerScope) {
layoutNode.requestRelayout()
@@ -108,6 +114,13 @@
}
}
+ internal fun observeSemanticsReads(
+ node: LayoutNode,
+ block: () -> Unit
+ ) {
+ observeReads(node, onCommitAffectingSemantics, block)
+ }
+
/**
* Observe snapshot reads for any target, allowing consumers to determine how to respond
* to state changes.
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 8c80e15..1cc3317 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
@@ -21,6 +21,10 @@
import androidx.compose.ui.layout.boundsInRoot
import androidx.compose.ui.semantics.SemanticsActions
import androidx.compose.ui.semantics.SemanticsConfiguration
+import androidx.compose.ui.semantics.SemanticsNode
+import androidx.compose.ui.semantics.SemanticsOwner
+import androidx.compose.ui.semantics.SemanticsPropertyKey
+import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.semantics.getOrNull
/**
@@ -32,31 +36,64 @@
*/
interface SemanticsModifierNode : DelegatableNode {
/**
- * The SemanticsConfiguration holds substantive data, especially a list of key/value pairs
- * such as (label -> "buttonName").
+ * Clears the semantics of all the descendant nodes and sets new semantics.
+ *
+ * In the merged semantics tree, this clears the semantic information provided
+ * by the node's descendants (but not those of the layout node itself, if any)
+ * In the unmerged tree, the semantics node is marked with
+ * "[SemanticsConfiguration.isClearingSemantics]", but nothing is actually cleared.
+ *
+ * Compose's default semantics provide baseline usability for screen-readers, but this can be
+ * used to provide a more polished screen-reader experience: for example, clearing the
+ * semantics of a group of tiny buttons, and setting equivalent actions on the card
+ * containing them.
*/
- val semanticsConfiguration: SemanticsConfiguration
+ @get:Suppress("GetterSetterNames")
+ val shouldClearDescendantSemantics: Boolean
+ get() = false
+
+ /**
+ * Whether the semantic information provided by this node and
+ * its descendants should be treated as one logical entity.
+ * Most commonly set on screen-reader-focusable items such as buttons or form fields.
+ * In the merged semantics tree, all descendant nodes (except those themselves marked
+ * [shouldMergeDescendantSemantics]) will disappear from the tree, and their properties
+ * will get merged into the parent's configuration (using a merging algorithm that varies based
+ * on the type of property -- for example, text properties will get concatenated, separated
+ * by commas). In the unmerged semantics tree, the node is simply marked with
+ * [SemanticsConfiguration.isMergingSemanticsOfDescendants].
+ */
+ @get:Suppress("GetterSetterNames")
+ val shouldMergeDescendantSemantics: Boolean
+ get() = false
+
+ /**
+ * Add semantics key/value pairs to the layout node, for use in testing, accessibility, etc.
+ *
+ * The [SemanticsPropertyReceiver] provides "key = value"-style setters for any
+ * [SemanticsPropertyKey]. Additionally, chaining multiple semantics modifiers is
+ * also a supported style.
+ *
+ * The resulting semantics produce two [SemanticsNode] trees:
+ *
+ * The "unmerged tree" rooted at [SemanticsOwner.unmergedRootSemanticsNode] has one
+ * [SemanticsNode] per layout node which has any [SemanticsModifierNode] on it. This
+ * [SemanticsNode] contains all the properties set in all the [SemanticsModifierNode]s on
+ * that node.
+ *
+ * The "merged tree" rooted at [SemanticsOwner.rootSemanticsNode] has equal-or-fewer nodes: it
+ * simplifies the structure based on [shouldMergeDescendantSemantics] and
+ * [shouldClearDescendantSemantics]. For most purposes (especially accessibility, or the
+ * testing of accessibility), the merged semantics tree should be used.
+ */
+ fun SemanticsPropertyReceiver.applySemantics()
}
-fun SemanticsModifierNode.invalidateSemantics() = requireOwner().onSemanticsChange()
-
-internal val SemanticsModifierNode.useMinimumTouchTarget: Boolean
- get() = semanticsConfiguration.getOrNull(SemanticsActions.OnClick) != null
+fun SemanticsModifierNode.invalidateSemantics() = requireLayoutNode().invalidateSemantics()
internal val SemanticsConfiguration.useMinimumTouchTarget: Boolean
get() = getOrNull(SemanticsActions.OnClick) != null
-internal fun SemanticsModifierNode.touchBoundsInRoot(): Rect {
- if (!node.isAttached) {
- return Rect.Zero
- }
- if (!useMinimumTouchTarget) {
- return requireCoordinator(Nodes.Semantics).boundsInRoot()
- }
-
- return requireCoordinator(Nodes.Semantics).touchBoundsInRoot()
-}
-
internal fun Modifier.Node.touchBoundsInRoot(useMinimumTouchTarget: Boolean): Rect {
if (!node.isAttached) {
return Rect.Zero
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsConfiguration.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsConfiguration.kt
index 0ca7a3dd..922af3f 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsConfiguration.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsConfiguration.kt
@@ -59,7 +59,15 @@
}
override fun <T> set(key: SemanticsPropertyKey<T>, value: T) {
- props[key] = value
+ if (value is AccessibilityAction<*> && contains(key)) {
+ val prev = props[key] as AccessibilityAction<*>
+ props[key] = AccessibilityAction(
+ value.label ?: prev.label,
+ value.action ?: prev.action
+ )
+ } else {
+ props[key] = value
+ }
}
operator fun <T> contains(key: SemanticsPropertyKey<T>): Boolean {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.kt
index 723d099..688113e 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.kt
@@ -47,16 +47,10 @@
}
internal object EmptySemanticsElement :
- ModifierNodeElement<CoreSemanticsModifierNode>() {
+ ModifierNodeElement<EmptySemanticsModifier>() {
+ override fun create() = EmptySemanticsModifier()
- private val semanticsConfiguration = SemanticsConfiguration().apply {
- isMergingSemanticsOfDescendants = false
- isClearingSemantics = false
- }
-
- override fun create() = CoreSemanticsModifierNode(semanticsConfiguration)
-
- override fun update(node: CoreSemanticsModifierNode) {}
+ override fun update(node: EmptySemanticsModifier) {}
override fun InspectorInfo.inspectableProperties() {
// Nothing to inspect.
@@ -67,8 +61,22 @@
}
internal class CoreSemanticsModifierNode(
- override var semanticsConfiguration: SemanticsConfiguration
-) : Modifier.Node(), SemanticsModifierNode
+ var mergeDescendants: Boolean,
+ var isClearingSemantics: Boolean,
+ var properties: SemanticsPropertyReceiver.() -> Unit
+) : Modifier.Node(), SemanticsModifierNode {
+ override val shouldClearDescendantSemantics: Boolean
+ get() = isClearingSemantics
+ override val shouldMergeDescendantSemantics: Boolean
+ get() = mergeDescendants
+ override fun SemanticsPropertyReceiver.applySemantics() {
+ properties()
+ }
+}
+
+internal class EmptySemanticsModifier : Modifier.Node(), SemanticsModifierNode {
+ override fun SemanticsPropertyReceiver.applySemantics() {}
+}
/**
* Add semantics key/value pairs to the layout node, for use in testing, accessibility, etc.
@@ -110,30 +118,33 @@
// Implement SemanticsModifier to allow tooling to inspect the semantics configuration
internal data class AppendedSemanticsElement(
- override val semanticsConfiguration: SemanticsConfiguration
+ val mergeDescendants: Boolean,
+ val properties: (SemanticsPropertyReceiver.() -> Unit)
) : ModifierNodeElement<CoreSemanticsModifierNode>(), SemanticsModifier {
- constructor(
- mergeDescendants: Boolean,
- properties: (SemanticsPropertyReceiver.() -> Unit)
- ) : this(
- SemanticsConfiguration().apply {
+ // This should only ever be called by layout inspector
+ override val semanticsConfiguration: SemanticsConfiguration
+ get() = SemanticsConfiguration().apply {
isMergingSemanticsOfDescendants = mergeDescendants
properties()
}
- )
override fun create(): CoreSemanticsModifierNode {
- return CoreSemanticsModifierNode(semanticsConfiguration)
+ return CoreSemanticsModifierNode(
+ mergeDescendants = mergeDescendants,
+ isClearingSemantics = false,
+ properties = properties
+ )
}
override fun update(node: CoreSemanticsModifierNode) {
- node.semanticsConfiguration = semanticsConfiguration
+ node.mergeDescendants = mergeDescendants
+ node.properties = properties
}
override fun InspectorInfo.inspectableProperties() {
name = "semantics"
- properties["mergeDescendants"] = semanticsConfiguration.isMergingSemanticsOfDescendants
+ properties["mergeDescendants"] = mergeDescendants
addSemanticsPropertiesFrom(semanticsConfiguration)
}
}
@@ -159,24 +170,27 @@
// Implement SemanticsModifier to allow tooling to inspect the semantics configuration
internal data class ClearAndSetSemanticsElement(
- override val semanticsConfiguration: SemanticsConfiguration
+ val properties: SemanticsPropertyReceiver.() -> Unit
) : ModifierNodeElement<CoreSemanticsModifierNode>(), SemanticsModifier {
- init {
- semanticsConfiguration.isMergingSemanticsOfDescendants = false
- semanticsConfiguration.isClearingSemantics = true
- }
-
- constructor(properties: (SemanticsPropertyReceiver.() -> Unit)) : this(
- SemanticsConfiguration().apply(properties)
- )
+ // This should only ever be called by layout inspector
+ override val semanticsConfiguration: SemanticsConfiguration
+ get() = SemanticsConfiguration().apply {
+ isMergingSemanticsOfDescendants = false
+ isClearingSemantics = true
+ properties()
+ }
override fun create(): CoreSemanticsModifierNode {
- return CoreSemanticsModifierNode(semanticsConfiguration)
+ return CoreSemanticsModifierNode(
+ mergeDescendants = false,
+ isClearingSemantics = true,
+ properties = properties
+ )
}
override fun update(node: CoreSemanticsModifierNode) {
- node.semanticsConfiguration = semanticsConfiguration
+ node.properties = properties
}
override fun InspectorInfo.inspectableProperties() {
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 41cdc4f..bb78b7e 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
@@ -402,7 +402,9 @@
}
val fakeNode = SemanticsNode(
outerSemanticsNode = object : SemanticsModifierNode, Modifier.Node() {
- override val semanticsConfiguration = configuration
+ override fun SemanticsPropertyReceiver.applySemantics() {
+ properties()
+ }
},
mergingEnabled = false,
layoutNode = LayoutNode(
@@ -427,25 +429,9 @@
}
}
-internal val LayoutNode.collapsedSemantics: SemanticsConfiguration?
- get() {
- var result: SemanticsConfiguration? = null
- nodes.tailToHead(Nodes.Semantics) {
- val current = result
- if (current == null || it.semanticsConfiguration.isClearingSemantics) {
- result = it.semanticsConfiguration
- } else {
- result = it.semanticsConfiguration.copy().also {
- it.collapsePeer(current)
- }
- }
- }
- return result
- }
-
internal val LayoutNode.outerMergingSemantics: SemanticsModifierNode?
get() = nodes.firstFromHead(Nodes.Semantics) {
- it.semanticsConfiguration.isMergingSemanticsOfDescendants
+ it.shouldMergeDescendantSemantics
}
/**
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 a5d43a5..7a39405 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
@@ -18,6 +18,7 @@
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.node.LayoutNode
+import androidx.compose.ui.node.Nodes
import androidx.compose.ui.util.fastForEach
/**
@@ -37,7 +38,16 @@
val unmergedRootSemanticsNode: SemanticsNode
get() {
- return SemanticsNode(rootNode, mergingEnabled = false)
+ return SemanticsNode(
+ outerSemanticsNode = rootNode.nodes.head(Nodes.Semantics)!!.node,
+ layoutNode = rootNode,
+ mergingEnabled = false,
+ // Forcing an empty SemanticsConfiguration here since the root node will always
+ // have an empty config, but if we don't pass this in explicitly here it will try
+ // to call `rootNode.collapsedSemantics` which will fail because the LayoutNode
+ // is not yet attached when this getter is first called.
+ unmergedConfig = SemanticsConfiguration()
+ )
}
}
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/DelegatingNodeTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/DelegatingNodeTest.kt
index 2fc9795..698d042 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/DelegatingNodeTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/DelegatingNodeTest.kt
@@ -21,7 +21,7 @@
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
-import androidx.compose.ui.semantics.SemanticsConfiguration
+import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.unit.Constraints
import org.junit.Test
import org.junit.runner.RunWith
@@ -830,8 +830,7 @@
}
class SemanticsMod(val id: String = "") : SemanticsModifierNode, Modifier.Node() {
- override val semanticsConfiguration: SemanticsConfiguration
- get() = SemanticsConfiguration()
+ override fun SemanticsPropertyReceiver.applySemantics() { }
override fun toString(): String {
return "SemanticsMod($id)"
}
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
index 586d600..df004df 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
@@ -61,6 +61,7 @@
import androidx.compose.ui.platform.invertTo
import androidx.compose.ui.semantics.SemanticsConfiguration
import androidx.compose.ui.semantics.SemanticsModifier
+import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
@@ -1382,12 +1383,11 @@
@Test
fun hitTestSemantics_pointerInMinimumTouchTarget_closestHit() {
- val semanticsConfiguration = SemanticsConfiguration()
val semanticsNode1 = object : SemanticsModifierNode, Modifier.Node() {
- override val semanticsConfiguration: SemanticsConfiguration = semanticsConfiguration
+ override fun SemanticsPropertyReceiver.applySemantics() { }
}
val semanticsNode2 = object : SemanticsModifierNode, Modifier.Node() {
- override val semanticsConfiguration: SemanticsConfiguration = semanticsConfiguration
+ override fun SemanticsPropertyReceiver.applySemantics() { }
}
data class TestSemanticsElement(
private val node: Modifier.Node
diff --git a/constraintlayout/constraintlayout-compose/build.gradle b/constraintlayout/constraintlayout-compose/build.gradle
index 639aa60..51a3271 100644
--- a/constraintlayout/constraintlayout-compose/build.gradle
+++ b/constraintlayout/constraintlayout-compose/build.gradle
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-import androidx.build.AndroidXComposePlugin
import androidx.build.LibraryType
-import androidx.build.Publish
plugins {
id("AndroidXPlugin")
@@ -24,77 +22,49 @@
id("AndroidXComposePlugin")
}
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+androidXMultiplatform {
+ android()
-dependencies {
- if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- implementation(project(":compose:ui:ui"))
- implementation(project(":compose:ui:ui-unit"))
- implementation(project(":compose:ui:ui-util"))
- implementation(project(":compose:foundation:foundation"))
- implementation(project(":compose:foundation:foundation-layout"))
-
- implementation(project(":constraintlayout:constraintlayout-core"))
-
- androidTestImplementation(project(":compose:material:material"))
- androidTestImplementation(project(":compose:ui:ui-test"))
- androidTestImplementation(project(":compose:ui:ui-test-junit4"))
- androidTestImplementation(project(":compose:ui:ui-test-manifest"))
- androidTestImplementation(project(":activity:activity"))
-
- androidTestImplementation(libs.kotlinTest)
- androidTestImplementation(libs.testRules)
- androidTestImplementation(libs.testRunner)
- androidTestImplementation(libs.junit)
-
- lintPublish(project(":constraintlayout:constraintlayout-compose-lint"))
- }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- androidXComposeMultiplatform {
- android()
- desktop()
- }
-
- kotlin {
- /*
- * When updating dependencies, make sure to make the an an analogous update in the
- * corresponding block above
- */
- sourceSets {
- commonMain.dependencies {
-// implementation(libs.kotlinStdlibCommon)
-
+ sourceSets {
+ commonMain {
+ dependencies {
implementation(project(":compose:ui:ui"))
- implementation("androidx.compose.ui:ui-unit:1.4.0-beta02")
- implementation("androidx.compose.ui:ui-util:1.4.0-beta02")
- implementation("androidx.compose.foundation:foundation:1.4.0-beta02")
- implementation("androidx.compose.foundation:foundation-layout:1.4.0-beta02")
+ implementation(project(":compose:ui:ui-unit"))
+ implementation(project(":compose:ui:ui-util"))
+ implementation(project(":compose:foundation:foundation"))
+ implementation(project(":compose:foundation:foundation-layout"))
implementation(project(":constraintlayout:constraintlayout-core"))
-
}
+ }
- androidMain.dependencies {
+ commonTest {
+ dependencies {
+ }
+ }
+
+ jvmMain {
+ dependencies {
+ }
+ }
+
+
+ androidMain {
+ dependsOn(commonMain)
+ dependsOn(jvmMain)
+ dependencies {
api("androidx.annotation:annotation:1.1.0")
implementation("androidx.core:core-ktx:1.5.0")
}
+ }
- desktopMain.dependencies {
- implementation(libs.kotlinStdlib)
+ jvmTest {
+ dependencies {
}
+ }
- // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
- // need to add Robolectric (which must be kept out of androidAndroidTest), use a top
- // level dependencies block instead:
- // `dependencies { testImplementation(libs.robolectric) }`
- androidTest.dependencies {
- implementation(libs.testRules)
- implementation(libs.testRunner)
- implementation(libs.junit)
- }
-
- androidAndroidTest.dependencies {
+ androidAndroidTest {
+ dependsOn(jvmTest)
+ dependencies {
implementation(libs.kotlinTest)
implementation(libs.testRules)
implementation(libs.testRunner)
@@ -107,9 +77,27 @@
implementation(project(":compose:test-utils"))
}
}
+
+
+ // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
+ // need to add Robolectric (which must be kept out of androidAndroidTest), use a top
+ // level dependencies block instead:
+ // `dependencies { testImplementation(libs.robolectric) }`
+ androidTest {
+ dependsOn(jvmTest)
+ dependencies {
+ implementation(libs.testRules)
+ implementation(libs.testRunner)
+ implementation(libs.junit)
+ }
+ }
}
}
+dependencies {
+ lintPublish(project(":constraintlayout:constraintlayout-compose-lint"))
+}
+
androidx {
name = "Android ConstraintLayout Compose Library"
type = LibraryType.PUBLISHED_LIBRARY
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayout.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayout.kt
index f730450..1896407 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayout.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayout.kt
@@ -40,6 +40,7 @@
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.neverEqualPolicy
import androidx.compose.runtime.remember
@@ -258,7 +259,7 @@
var endConstraint by remember { mutableStateOf(constraintSet) }
val progress = remember { Animatable(0.0f) }
val channel = remember { Channel<ConstraintSet>(Channel.CONFLATED) }
- val direction = remember { mutableStateOf(1) }
+ val direction = remember { mutableIntStateOf(1) }
SideEffect {
channel.trySend(constraintSet)
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionDragHandler.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionDragHandler.kt
index 3328406..40561ea 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionDragHandler.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionDragHandler.kt
@@ -24,6 +24,7 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.util.VelocityTracker
+import androidx.compose.ui.input.pointer.util.addPointerInputChange
import androidx.compose.ui.platform.debugInspectorInfo
import androidx.compose.ui.unit.Velocity
import kotlinx.coroutines.channels.Channel
@@ -104,7 +105,7 @@
)
}
) { change, dragAmount ->
- velocityTracker.addPosition(change.uptimeMillis, change.position)
+ velocityTracker.addPointerInputChange(change)
// As dragging is done, pass the dragAmount to update the MotionLayout progress.
dragChannel.trySend(MotionDragState.onDrag(dragAmount))
}
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/carousel/CarouselSwipeable.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/carousel/CarouselSwipeable.kt
index 42515c3..3c7ee4d 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/carousel/CarouselSwipeable.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/carousel/CarouselSwipeable.kt
@@ -30,6 +30,7 @@
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.Saver
@@ -105,12 +106,12 @@
val overflow: FloatState get() = overflowState
// Use `Float.NaN` as a placeholder while the state is uninitialised.
- private val offsetState = mutableStateOf(0f)
- private val overflowState = mutableStateOf(0f)
+ private val offsetState = mutableFloatStateOf(0f)
+ private val overflowState = mutableFloatStateOf(0f)
// the source of truth for the "real"(non ui) position
// basically position in bounds + overflow
- private val absoluteOffset = mutableStateOf(0f)
+ private val absoluteOffset = mutableFloatStateOf(0f)
// current animation target, if animating, otherwise null
private val animationTarget = mutableStateOf<Float?>(null)
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/motion/utils/StopLogicEngine.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/motion/utils/StopLogicEngine.java
index 0982b43..40f75d0 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/motion/utils/StopLogicEngine.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/motion/utils/StopLogicEngine.java
@@ -34,6 +34,7 @@
private boolean mBackwards = false;
private float mStartPosition;
private float mLastPosition;
+ private float mLastTime;
@SuppressWarnings("unused")
private boolean mDone = false;
private static final float EPSILON = 0.00001f;
@@ -106,14 +107,14 @@
return mStage2Velocity + (mStage3Velocity - mStage2Velocity) * x / mStage2Duration;
}
if (mNumberOfStages == 2) {
- return mStage2EndPosition;
+ return 0;
}
x -= mStage2Duration;
if (x < mStage3Duration) {
return mStage3Velocity - mStage3Velocity * x / mStage3Duration;
}
- return mStage3EndPosition;
+ return 0;
}
private float calcY(float time) {
@@ -162,13 +163,14 @@
@Override
public float getInterpolation(float v) {
float y = calcY(v);
- mLastPosition = v;
+ mLastPosition = y;
+ mLastTime = v;
return mBackwards ? mStartPosition - y : mStartPosition + y;
}
@Override
public float getVelocity() {
- return mBackwards ? -getVelocity(mLastPosition) : getVelocity(mLastPosition);
+ return mBackwards ? -getVelocity(mLastTime) : getVelocity(mLastTime);
}
@Override
diff --git a/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/motion/MotionBasicTest.java b/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/motion/MotionBasicTest.java
index b9dd2fd..857999a 100644
--- a/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/motion/MotionBasicTest.java
+++ b/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/motion/MotionBasicTest.java
@@ -16,6 +16,7 @@
package androidx.constraintlayout.core.motion;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import androidx.constraintlayout.core.motion.utils.CurveFit;
import androidx.constraintlayout.core.motion.utils.Easing;
@@ -214,6 +215,7 @@
float pos = breakLogic.getInterpolation(time);
ret[i] = pos;
}
+ assertTrue(breakLogic.isStopped());
return ret;
}
diff --git a/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/motion/StopLogicTest.java b/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/motion/StopLogicTest.java
index 760085c..92f6313 100644
--- a/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/motion/StopLogicTest.java
+++ b/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/motion/StopLogicTest.java
@@ -40,6 +40,7 @@
float damping = 50f;
float stopThreshold = 0f;
int springBoundary = 0;
+ boolean expectStopped = false; // Doesn't make it to 1.0f in the given time
stop.springConfig(position,
destination,
currentVelocity,
@@ -65,7 +66,7 @@
+ "| ************* |\n"
+ "| *| 0.885\n"
+ "0.0 0.885\n";
- assertEquals(expect, verify(stop, position, maxTime));
+ assertEquals(expect, verify(stop, position, maxTime, expectStopped));
}
@Test
@@ -78,6 +79,7 @@
float maxTime = 0.9f;
float maxAcceleration = 3.2f;
float maxVelocity = 3.2f;
+ boolean expectStopped = true;
stop.config(position, destination, currentVelocity, maxTime, maxAcceleration, maxVelocity);
System.out.println(stop.debug("check1", 0));
String expect = ""
@@ -97,7 +99,7 @@
+ "| ************************* |\n"
+ "| *********************** *| 1.0\n"
+ "0.0 0.885\n";
- assertEquals(expect, verify(stop, position, maxTime));
+ assertEquals(expect, verify(stop, position, maxTime, expectStopped));
}
@Test
@@ -110,6 +112,7 @@
float maxTime = 0.9f;
float maxAcceleration = 3.2f;
float maxVelocity = 3.2f;
+ boolean expectStopped = true;
stop.config(position, destination, currentVelocity, maxTime, maxAcceleration, maxVelocity);
System.out.println(stop.debug("check1", 0));
String expect = ""
@@ -129,7 +132,7 @@
+ "| ************** |\n"
+ "| ****************************** *| 1.0\n"
+ "0.0 0.885\n";
- assertEquals(expect, verify(stop, position, maxTime));
+ assertEquals(expect, verify(stop, position, maxTime, expectStopped));
}
@Test
@@ -141,6 +144,7 @@
float maxTime = 0.9f;
float maxAcceleration = 3.2f;
float maxVelocity = 3.2f;
+ boolean expectStopped = true;
stop.config(position, destination, currentVelocity, maxTime, maxAcceleration, maxVelocity);
System.out.println(stop.debug("check1", 0));
String expect = ""
@@ -160,7 +164,7 @@
+ "| ****** |\n"
+ "| ************************************************** *| 1.0\n"
+ "0.0 0.885\n";
- assertEquals(expect, verify(stop, position, maxTime));
+ assertEquals(expect, verify(stop, position, maxTime, expectStopped));
}
@Test
@@ -172,6 +176,7 @@
float maxTime = 0.9f;
float maxAcceleration = 3.2f;
float maxVelocity = 1.2f;
+ boolean expectStopped = false; // Doesn't make it to 1f in the given time
stop.config(position, destination, currentVelocity, maxTime, maxAcceleration, maxVelocity);
System.out.println(stop.debug("check1", 0));
String expect = ""
@@ -191,7 +196,7 @@
+ "| *********** |\n"
+ "| *| 0.997\n"
+ "0.0 0.885\n";
- assertEquals(expect, verify(stop, position, maxTime));
+ assertEquals(expect, verify(stop, position, maxTime, expectStopped));
}
@Test
@@ -203,6 +208,7 @@
float maxTime = 0.9f;
float maxAcceleration = 3.2f;
float maxVelocity = 3.2f;
+ boolean expectStopped = true;
stop.config(position, destination, currentVelocity, maxTime, maxAcceleration, maxVelocity);
System.out.println(stop.debug("check1", 0));
String expect = ""
@@ -222,7 +228,7 @@
+ "| ************** |\n"
+ "| *| 1.0\n"
+ "0.0 0.885\n";
- assertEquals(expect, verify(stop, position, maxTime));
+ assertEquals(expect, verify(stop, position, maxTime, expectStopped));
}
@Test
@@ -234,6 +240,7 @@
float maxTime = 0.9f;
float maxAcceleration = 5.2f;
float maxVelocity = 1.2f;
+ boolean expectStopped = true;
stop.config(position, destination, currentVelocity, maxTime, maxAcceleration, maxVelocity);
System.out.println(stop.debug("check1", 0));
String expect = ""
@@ -253,10 +260,13 @@
+ "| ********** |\n"
+ "| ****** *| 1.0\n"
+ "0.0 0.885\n";
- assertEquals(expect, verify(stop, position, maxTime));
+ assertEquals(expect, verify(stop, position, maxTime, expectStopped));
}
- private static String verify(StopEngine stop, float position, float maxTime) {
+ private static String verify(StopEngine stop,
+ float position,
+ float maxTime,
+ boolean expectStopped) {
float p = stop.getInterpolation(0);
assertEquals(p, position, 0.0001);
int count = 60;
@@ -272,6 +282,7 @@
}
String ret = textDraw(count, count / 4, x, y, false);
System.out.println(ret);
+ assertEquals(expectStopped, stop.isStopped());
return ret;
}
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java
index f956ce5..9da084d 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java
@@ -17,6 +17,7 @@
package androidx.constraintlayout.motion.widget;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
import static androidx.constraintlayout.motion.widget.MotionScene.Transition.TRANSITION_FLAG_FIRST_DRAW;
import static androidx.constraintlayout.motion.widget.MotionScene.Transition.TRANSITION_FLAG_INTERCEPT_TOUCH;
import static androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.PARENT_ID;
@@ -504,8 +505,8 @@
* <table summary="Variant attributes" >
* <tr>
* <td>[ConstraintLayout attributes]</td>
- * <td>see {@see androidx.constraintlayout.widget.
- * ConstraintLayout ConstraintLayout} for attributes</td>
+ * <td>Also see {@link ConstraintLayout.LayoutParams
+ * ConstraintLayout.LayoutParams} for attributes</td>
* </tr>
* </table>
*
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintSet.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintSet.java
index 63d4f6c..4b86246 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintSet.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintSet.java
@@ -1507,9 +1507,9 @@
sMapToConstant.append(R.styleable.Layout_layout_constraintHeight,
LAYOUT_CONSTRAINT_HEIGHT);
sMapToConstant.append(R.styleable.Layout_layout_constrainedWidth,
- LAYOUT_CONSTRAINT_WIDTH);
+ CONSTRAINED_WIDTH);
sMapToConstant.append(R.styleable.Layout_layout_constrainedHeight,
- LAYOUT_CONSTRAINT_HEIGHT);
+ CONSTRAINED_HEIGHT);
sMapToConstant.append(R.styleable.Layout_layout_wrapBehaviorInParent,
LAYOUT_WRAP_BEHAVIOR);
diff --git a/core/core/src/androidTest/java/androidx/core/view/OWNERS b/core/core/src/androidTest/java/androidx/core/view/OWNERS
index da18aa6..23e355a 100644
--- a/core/core/src/androidTest/java/androidx/core/view/OWNERS
+++ b/core/core/src/androidTest/java/androidx/core/view/OWNERS
@@ -1,2 +1,3 @@
# Bug component: 461355
-per-file AccessibilityDelegateCompatTest.java = file:accessibility/OWNERS
\ No newline at end of file
+per-file AccessibilityDelegateCompatTest.java = file:accessibility/OWNERS
+per-file ViewCompatTest.java = file:accessibility/OWNERS
\ No newline at end of file
diff --git a/core/core/src/main/java/androidx/core/view/OWNERS b/core/core/src/main/java/androidx/core/view/OWNERS
index 1e4e68f..daf6ad0 100644
--- a/core/core/src/main/java/androidx/core/view/OWNERS
+++ b/core/core/src/main/java/androidx/core/view/OWNERS
@@ -1,2 +1,3 @@
# Bug component: 461355
-per-file AccessibilityDelegateCompat.java = file:accessibility/OWNERS
\ No newline at end of file
+per-file AccessibilityDelegateCompat.java = file:accessibility/OWNERS
+per-file ViewCompat.java = file:accessibility/OWNERS
\ No newline at end of file
diff --git a/core/core/src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java b/core/core/src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java
index a43c2af..5ccc392 100644
--- a/core/core/src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java
+++ b/core/core/src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java
@@ -207,24 +207,23 @@
insetsController = mView.getWindowInsetsController();
}
if (insetsController != null) {
- if (SDK_INT <= 33) {
- final AtomicBoolean isImeInsetsControllable = new AtomicBoolean(false);
- final WindowInsetsController.OnControllableInsetsChangedListener listener =
- (windowInsetsController, typeMask) -> isImeInsetsControllable.set(
- (typeMask & WindowInsetsCompat.Type.IME) != 0);
- // Register the OnControllableInsetsChangedListener would synchronously
- // callback current controllable insets. Adding the listener here to check if
- // ime inset is controllable.
- insetsController.addOnControllableInsetsChangedListener(listener);
- if (!isImeInsetsControllable.get()) {
- final InputMethodManager imm = (InputMethodManager) mView.getContext()
- .getSystemService(Context.INPUT_METHOD_SERVICE);
- // This is a backport when the app is in multi-windowing mode, it cannot
- // control the ime insets. Use the InputMethodManager instead.
- imm.hideSoftInputFromWindow(mView.getWindowToken(), 0);
- }
- insetsController.removeOnControllableInsetsChangedListener(listener);
+ final AtomicBoolean isImeInsetsControllable = new AtomicBoolean(false);
+ final WindowInsetsController.OnControllableInsetsChangedListener listener =
+ (windowInsetsController, typeMask) -> isImeInsetsControllable.set(
+ (typeMask & WindowInsetsCompat.Type.IME) != 0);
+ // Register the OnControllableInsetsChangedListener would synchronously
+ // callback current controllable insets. Adding the listener here to check if
+ // ime inset is controllable.
+ insetsController.addOnControllableInsetsChangedListener(listener);
+ if (!isImeInsetsControllable.get()) {
+ final InputMethodManager imm = (InputMethodManager) mView.getContext()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ // This is a backport when the app is in multi-windowing mode, it cannot
+ // control the ime insets. Use the InputMethodManager instead.
+ // TODO(b/280532442): Fix this in the platform side.
+ imm.hideSoftInputFromWindow(mView.getWindowToken(), 0);
}
+ insetsController.removeOnControllableInsetsChangedListener(listener);
insetsController.hide(WindowInsets.Type.ime());
} else {
// Couldn't find an insets controller, fallback to old implementation
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingCapabilities.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingCapabilities.kt
index 5ef3b7b..b7e35fd 100644
--- a/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingCapabilities.kt
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingCapabilities.kt
@@ -22,8 +22,8 @@
* @property isAzimuthalAngleSupported - Whether azimuthal angle of arrival is supported
* @property isElevationAngleSupported - Whether elevation angle of arrival is supported
* @property minRangingInterval - Minimum ranging interval
- * @property supportedChannels - Array of supported channels
- * @property supportedConfigIds - Array of supported config ids
+ * @property supportedChannels - Set of supported channels
+ * @property supportedConfigIds - Set of supported config ids
**/
class RangingCapabilities(
val isDistanceSupported: Boolean,
diff --git a/credentials/credentials-play-services-auth/build.gradle b/credentials/credentials-play-services-auth/build.gradle
index 7fd1e0e..4558801 100644
--- a/credentials/credentials-play-services-auth/build.gradle
+++ b/credentials/credentials-play-services-auth/build.gradle
@@ -31,11 +31,17 @@
}
// Closed source dependencies
- implementation(libs.playServicesAuth) {
+ implementation(libs.playServicesAuth){
exclude group: "androidx.loader"
exclude group: "androidx.fragment"
+ exclude group: "androidx.core"
}
- implementation(libs.playServicesFido)
+
+ implementation(libs.playServicesFido){
+ exclude group: "androidx.loader"
+ exclude group: "androidx.fragment"
+ exclude group: "androidx.core"
+ }
androidTestImplementation(libs.junit)
androidTestImplementation(libs.testExtJunit)
@@ -47,6 +53,9 @@
androidTestImplementation(libs.multidex)
androidTestImplementation(project(":internal-testutils-truth"))
androidTestImplementation(libs.kotlinCoroutinesAndroid)
+ androidTestImplementation("androidx.core:core-ktx:1.10.0")
+ androidTestImplementation("androidx.fragment:fragment:1.5.7")
+ androidTestImplementation("androidx.fragment:fragment-ktx:1.5.7")
}
android {
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt
index 1d74750..cf5f665 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt
@@ -35,7 +35,7 @@
import androidx.credentials.GetCredentialRequest
import androidx.credentials.GetPasswordOption
import androidx.credentials.GetPublicKeyCredentialOption
-import androidx.credentials.playservices.controllers.CreatePublicKeyCredential.PublicKeyCredentialControllerUtility.Companion.convertToPlayAuthPasskeyRequest
+import androidx.credentials.playservices.controllers.CreatePublicKeyCredential.PublicKeyCredentialControllerUtility.Companion.convertToPlayAuthPasskeyJsonRequest
import com.google.android.gms.auth.api.identity.BeginSignInRequest
import com.google.android.gms.auth.api.identity.BeginSignInRequest.GoogleIdTokenRequestOptions
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
@@ -64,8 +64,8 @@
)
autoSelect = autoSelect || option.isAutoSelectAllowed
} else if (option is GetPublicKeyCredentialOption && !isPublicKeyCredReqFound) {
- requestBuilder.setPasskeysSignInRequestOptions(
- convertToPlayAuthPasskeyRequest(option)
+ requestBuilder.setPasskeyJsonSignInRequestOptions(
+ convertToPlayAuthPasskeyJsonRequest(option)
)
isPublicKeyCredReqFound = true
// TODO(b/262924507) : watch for GIS update on single vs multiple options of a
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
index 64039cd..dcea304 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
@@ -170,6 +170,13 @@
callback.onError(e)
}
}
+ } catch (t: Throwable) {
+ val e = GetCredentialUnknownException(t.message)
+ cancelOrCallbackExceptionOrResult(cancellationSignal) {
+ executor.execute {
+ callback.onError(e)
+ }
+ }
}
}
@@ -188,13 +195,9 @@
} else if (response.googleIdToken != null) {
cred = createGoogleIdCredential(response)
} else if (response.publicKeyCredential != null) {
- try {
- cred = PublicKeyCredential(
- PublicKeyCredentialControllerUtility.toAssertPasskeyResponse(response)
- )
- } catch (t: Throwable) {
- throw GetCredentialUnknownException(t.message)
- }
+ cred = PublicKeyCredential(
+ PublicKeyCredentialControllerUtility.toAssertPasskeyResponse(response)
+ )
} else {
Log.w(TAG, "Credential returned but no google Id or password or passkey found")
}
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
index 613f392..893b7e6 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
@@ -22,6 +22,8 @@
import androidx.credentials.GetPublicKeyCredentialOption
import androidx.credentials.exceptions.CreateCredentialCancellationException
import androidx.credentials.exceptions.CreateCredentialException
+import androidx.credentials.exceptions.GetCredentialCancellationException
+import androidx.credentials.exceptions.GetCredentialException
import androidx.credentials.exceptions.domerrors.AbortError
import androidx.credentials.exceptions.domerrors.ConstraintError
import androidx.credentials.exceptions.domerrors.DataError
@@ -35,6 +37,7 @@
import androidx.credentials.exceptions.domerrors.TimeoutError
import androidx.credentials.exceptions.domerrors.UnknownError
import androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialDomException
+import androidx.credentials.exceptions.publickeycredential.GetPublicKeyCredentialDomException
import com.google.android.gms.auth.api.identity.BeginSignInRequest
import com.google.android.gms.auth.api.identity.SignInCredential
import com.google.android.gms.fido.common.Transport
@@ -162,7 +165,7 @@
if (clientExtensionResults != null) {
try {
val uvmEntries = clientExtensionResults.uvmEntries
- val uvmEntriesList = uvmEntries.uvmEntryList
+ val uvmEntriesList = uvmEntries?.uvmEntryList
if (uvmEntriesList != null) {
val uvmEntriesJSON = JSONArray()
for (entry in uvmEntriesList) {
@@ -185,64 +188,65 @@
fun toAssertPasskeyResponse(cred: SignInCredential): String {
val json = JSONObject()
val publicKeyCred = cred.publicKeyCredential
- val authenticatorResponse = publicKeyCred?.response!!
- if (authenticatorResponse is AuthenticatorAssertionResponse) {
- val responseJson = JSONObject()
- responseJson.put(
- "clientDataJSON",
- b64Encode(authenticatorResponse.clientDataJSON)
- )
- responseJson.put(
- "authenticatorData",
- b64Encode(authenticatorResponse.authenticatorData)
- )
- responseJson.put(
- "signature",
- b64Encode(authenticatorResponse.signature)
- )
- authenticatorResponse.userHandle?.let {
- responseJson.put(
- "userHandle", b64Encode(authenticatorResponse.userHandle!!)
- )
+ when (val authenticatorResponse = publicKeyCred?.response!!) {
+ is AuthenticatorErrorResponse -> {
+ throw beginSignInPublicKeyCredentialResponseContainsError(
+ authenticatorResponse)
}
- // TODO(b/262924507) : attestation object missing in fido impl
- json.put("response", responseJson)
- } else {
+ is AuthenticatorAssertionResponse -> {
+ beginSignInAssertionResponse(authenticatorResponse, json, publicKeyCred)
+ }
+ else -> {
Log.e(
TAG,
"AuthenticatorResponse expected assertion response but " +
"got: ${authenticatorResponse.javaClass.name}")
+ }
}
+ return json.toString()
+ }
+
+ private fun beginSignInAssertionResponse(
+ authenticatorResponse: AuthenticatorAssertionResponse,
+ json: JSONObject,
+ publicKeyCred: PublicKeyCredential
+ ) {
+ val responseJson = JSONObject()
+ responseJson.put(
+ "clientDataJSON",
+ b64Encode(authenticatorResponse.clientDataJSON)
+ )
+ responseJson.put(
+ "authenticatorData",
+ b64Encode(authenticatorResponse.authenticatorData)
+ )
+ responseJson.put(
+ "signature",
+ b64Encode(authenticatorResponse.signature)
+ )
+ authenticatorResponse.userHandle?.let {
+ responseJson.put(
+ "userHandle", b64Encode(authenticatorResponse.userHandle!!)
+ )
+ }
+ // TODO(b/262924507) : attestation object missing in fido impl
+ json.put("response", responseJson)
json.put("id", publicKeyCred.id)
json.put("rawId", b64Encode(publicKeyCred.rawId))
json.put("type", publicKeyCred.type)
- return json.toString()
}
/**
* Converts from the Credential Manager public key credential option to the Play Auth
- * Module passkey option.
+ * Module passkey json option.
*
- * @throws JSONException If rpId or challenge either do not
- * exist or are empty in the initial request json
*/
- fun convertToPlayAuthPasskeyRequest(request: GetPublicKeyCredentialOption):
- BeginSignInRequest.PasskeysRequestOptions {
- // TODO(b/262924507) : Make sure this is in compliance with w3 as impl continues
- // TODO(b/262924507) : Improve codebase readability as done here
- // (readable error capture + docs/etc)
- val json = JSONObject(request.requestJson)
- val rpId = json.optString("rpId", "")
- if (rpId.isEmpty()) {
- throw JSONException("GetPublicKeyCredentialOption - rpId not specified in the " +
- "request or is unexpectedly empty")
- }
- val challenge = getChallenge(json)
- return BeginSignInRequest.PasskeysRequestOptions.Builder()
+ fun convertToPlayAuthPasskeyJsonRequest(option: GetPublicKeyCredentialOption):
+ BeginSignInRequest.PasskeyJsonRequestOptions {
+ return BeginSignInRequest.PasskeyJsonRequestOptions.Builder()
.setSupported(true)
- .setRpId(rpId)
- .setChallenge(challenge)
+ .setRequestJson(option.requestJson)
.build()
}
@@ -292,6 +296,34 @@
return null
}
+ // Helper method for the begin sign in flow to identify an authenticator error response
+ private fun beginSignInPublicKeyCredentialResponseContainsError(
+ authenticatorResponse: AuthenticatorErrorResponse
+ ): GetCredentialException {
+ val code = authenticatorResponse.errorCode
+ var exceptionError = orderedErrorCodeToExceptions[code]
+ var msg = authenticatorResponse.errorMessage
+ val exception: GetCredentialException
+ if (exceptionError == null) {
+ exception = GetPublicKeyCredentialDomException(
+ UnknownError(), "unknown fido gms exception - $msg"
+ )
+ } else {
+ // This fix is quite fragile because it relies on that the fido module
+ // does not change its error message, but is the only viable solution
+ // because there's no other differentiator.
+ if (code == ErrorCode.CONSTRAINT_ERR &&
+ msg?.contains("Unable to get sync account") == true
+ ) {
+ exception = GetCredentialCancellationException(
+ "Passkey retrieval was cancelled by the user.")
+ } else {
+ exception = GetPublicKeyCredentialDomException(exceptionError, msg)
+ }
+ }
+ return exception
+ }
+
internal fun parseOptionalExtensions(
json: JSONObject,
builder: PublicKeyCredentialCreationOptions.Builder
@@ -536,4 +568,4 @@
ErrorCode.TIMEOUT_ERR to TimeoutError()
)
}
-}
\ No newline at end of file
+}
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 4624848..569d1ac 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -30,14 +30,14 @@
docs("androidx.asynclayoutinflater:asynclayoutinflater:1.1.0-alpha01")
docs("androidx.asynclayoutinflater:asynclayoutinflater-appcompat:1.1.0-alpha01")
docs("androidx.autofill:autofill:1.2.0-beta01")
- docs("androidx.benchmark:benchmark-common:1.2.0-alpha13")
- docs("androidx.benchmark:benchmark-junit4:1.2.0-alpha13")
- docs("androidx.benchmark:benchmark-macro:1.2.0-alpha13")
- docs("androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha13")
+ docs("androidx.benchmark:benchmark-common:1.2.0-alpha14")
+ docs("androidx.benchmark:benchmark-junit4:1.2.0-alpha14")
+ docs("androidx.benchmark:benchmark-macro:1.2.0-alpha14")
+ docs("androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha14")
docs("androidx.biometric:biometric:1.2.0-alpha05")
docs("androidx.biometric:biometric-ktx:1.2.0-alpha05")
samples("androidx.biometric:biometric-ktx-samples:1.2.0-alpha05")
- docs("androidx.browser:browser:1.5.0")
+ docs("androidx.browser:browser:1.6.0-alpha01")
docs("androidx.camera:camera-camera2:1.3.0-alpha06")
docs("androidx.camera:camera-core:1.3.0-alpha06")
docs("androidx.camera:camera-extensions:1.3.0-alpha06")
@@ -252,18 +252,18 @@
docs("androidx.navigation:navigation-testing:2.6.0-beta01")
docs("androidx.navigation:navigation-ui:2.6.0-beta01")
docs("androidx.navigation:navigation-ui-ktx:2.6.0-beta01")
- docs("androidx.paging:paging-common:3.2.0-alpha04")
- docs("androidx.paging:paging-common-ktx:3.2.0-alpha04")
- docs("androidx.paging:paging-compose:1.0.0-alpha18")
- samples("androidx.paging:paging-compose-samples:3.0.0-alpha08")
- docs("androidx.paging:paging-guava:3.2.0-alpha04")
- docs("androidx.paging:paging-runtime:3.2.0-alpha04")
- docs("androidx.paging:paging-runtime-ktx:3.2.0-alpha04")
- docs("androidx.paging:paging-rxjava2:3.2.0-alpha04")
- docs("androidx.paging:paging-rxjava2-ktx:3.2.0-alpha04")
- docs("androidx.paging:paging-rxjava3:3.2.0-alpha04")
- samples("androidx.paging:paging-samples:3.2.0-alpha04")
- docs("androidx.paging:paging-testing:3.2.0-alpha04")
+ docs("androidx.paging:paging-common:3.2.0-alpha05")
+ docs("androidx.paging:paging-common-ktx:3.2.0-alpha05")
+ docs("androidx.paging:paging-compose:1.0.0-alpha19")
+ samples("androidx.paging:paging-compose-samples:1.0.0-alpha19")
+ docs("androidx.paging:paging-guava:3.2.0-alpha05")
+ docs("androidx.paging:paging-runtime:3.2.0-alpha05")
+ docs("androidx.paging:paging-runtime-ktx:3.2.0-alpha05")
+ docs("androidx.paging:paging-rxjava2:3.2.0-alpha05")
+ docs("androidx.paging:paging-rxjava2-ktx:3.2.0-alpha05")
+ docs("androidx.paging:paging-rxjava3:3.2.0-alpha05")
+ samples("androidx.paging:paging-samples:3.2.0-alpha05")
+ docs("androidx.paging:paging-testing:3.2.0-alpha05")
docs("androidx.palette:palette:1.0.0")
docs("androidx.palette:palette-ktx:1.0.0")
docs("androidx.percentlayout:percentlayout:1.0.1")
@@ -272,7 +272,6 @@
docs("androidx.print:print:1.1.0-beta01")
docs("androidx.privacysandbox.ads:ads-adservices:1.0.0-beta03")
docs("androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta03")
- docs("androidx.privacysandbox.plugins:plugins-privacysandbox-library:1.0.0-alpha01")
docs("androidx.privacysandbox.sdkruntime:sdkruntime-client:1.0.0-alpha03")
docs("androidx.privacysandbox.sdkruntime:sdkruntime-core:1.0.0-alpha03")
docs("androidx.privacysandbox.tools:tools:1.0.0-alpha03")
@@ -340,10 +339,10 @@
docs("androidx.test.services:storage:1.5.0-alpha01")
docs("androidx.test.uiautomator:uiautomator:2.3.0-alpha03")
docs("androidx.textclassifier:textclassifier:1.0.0-alpha04")
- docs("androidx.tracing:tracing:1.2.0-beta03")
- docs("androidx.tracing:tracing-ktx:1.2.0-beta03")
- docs("androidx.tracing:tracing-perfetto:1.0.0-alpha14")
- docs("androidx.tracing:tracing-perfetto-common:1.0.0-alpha14")
+ docs("androidx.tracing:tracing:1.2.0-beta04")
+ docs("androidx.tracing:tracing-ktx:1.2.0-beta04")
+ docs("androidx.tracing:tracing-perfetto:1.0.0-alpha15")
+ docs("androidx.tracing:tracing-perfetto-common:1.0.0-alpha15")
docs("androidx.transition:transition:1.4.1")
docs("androidx.transition:transition-ktx:1.4.1")
docs("androidx.tv:tv-foundation:1.0.0-alpha06")
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-sk/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-sk/strings.xml
index a6775dc..57e00cd 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-sk/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-sk/strings.xml
@@ -18,7 +18,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="emoji_category_recent" msgid="7142376595414250279">"NEDÁVNO POUŽITÉ"</string>
- <string name="emoji_category_emotions" msgid="1570830970240985537">"SMAJLÍKY A EMOTIKONY"</string>
+ <string name="emoji_category_emotions" msgid="1570830970240985537">"SMAJLÍKY A EMÓCIE"</string>
<string name="emoji_category_people" msgid="7968173366822927025">"ĽUDIA"</string>
<string name="emoji_category_animals_nature" msgid="4640771324837307541">"ZVIERATÁ A PRÍRODA"</string>
<string name="emoji_category_food_drink" msgid="1189971856721244395">"JEDLO A NÁPOJE"</string>
diff --git a/emoji2/integration-tests/init-enabled-macrobenchmark-target/build.gradle b/emoji2/integration-tests/init-enabled-macrobenchmark-target/build.gradle
index d2fff66..52f133f 100644
--- a/emoji2/integration-tests/init-enabled-macrobenchmark-target/build.gradle
+++ b/emoji2/integration-tests/init-enabled-macrobenchmark-target/build.gradle
@@ -34,7 +34,7 @@
dependencies {
implementation(libs.kotlinStdlib)
implementation(libs.constraintLayout, { transitive = true })
- implementation(project(":arch:core:core-runtime"))
+ implementation("androidx.arch.core:core-runtime:2.2.0")
implementation(project(":appcompat:appcompat"))
implementation(project(":profileinstaller:profileinstaller"))
implementation(libs.material)
diff --git a/fragment/CHANGELOG.md b/fragment/CHANGELOG.md
new file mode 100644
index 0000000..0ea055d
--- /dev/null
+++ b/fragment/CHANGELOG.md
@@ -0,0 +1,22 @@
+# Log for changes in the Fragment library
+#
+# `Added`: for new features
+# `Changed`: for changes in existing functionality
+# `Deprecated`: for soon to be removed functionality
+# `Removed`: for now removed feature
+# `Fixed`: for any bug fixes
+# `Security`: in case of vulnerabilities
+#
+# Possible headings:
+# API Changes
+# Bug Fixes
+# Dependency Updates
+# Behavior Change
+# External Contributions
+
+# Unreleased
+
+### Dependency Updates
+
+* Changed dependency of Activity library from version 1.5.1 to version 1.7.1.
+
diff --git a/fragment/fragment-ktx/build.gradle b/fragment/fragment-ktx/build.gradle
index 3eb971a..7327886 100644
--- a/fragment/fragment-ktx/build.gradle
+++ b/fragment/fragment-ktx/build.gradle
@@ -25,7 +25,7 @@
dependencies {
api(project(":fragment:fragment"))
- api("androidx.activity:activity-ktx:1.5.1") {
+ api("androidx.activity:activity-ktx:1.7.1") {
because "Mirror fragment dependency graph for -ktx artifacts"
}
api("androidx.core:core-ktx:1.2.0") {
diff --git a/fragment/fragment/build.gradle b/fragment/fragment/build.gradle
index 248f510..91b6c0f 100644
--- a/fragment/fragment/build.gradle
+++ b/fragment/fragment/build.gradle
@@ -29,7 +29,7 @@
api("androidx.collection:collection:1.1.0")
api("androidx.viewpager:viewpager:1.0.0")
api("androidx.loader:loader:1.0.0")
- api("androidx.activity:activity:1.5.1")
+ api("androidx.activity:activity:1.7.1")
api("androidx.lifecycle:lifecycle-runtime:2.6.1")
api("androidx.lifecycle:lifecycle-livedata-core:2.6.1")
api("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
diff --git a/fragment/integration-tests/testapp/build.gradle b/fragment/integration-tests/testapp/build.gradle
index 17004dc..79ee5ac 100644
--- a/fragment/integration-tests/testapp/build.gradle
+++ b/fragment/integration-tests/testapp/build.gradle
@@ -25,7 +25,8 @@
main {
res.srcDirs = ["src/main/res",
"src/main/res/layouts/doubleTransitionBug",
- "src/main/res/layouts/kittenFragmentTransitions"
+ "src/main/res/layouts/kittenFragmentTransitions",
+ "src/main/res/layouts/basicAnimators",
]
}
}
diff --git a/fragment/integration-tests/testapp/src/main/AndroidManifest.xml b/fragment/integration-tests/testapp/src/main/AndroidManifest.xml
index 19d7b02..331b806 100644
--- a/fragment/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/fragment/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -21,6 +21,7 @@
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true"
+ android:enableOnBackInvokedCallback="true"
tools:ignore="AllowBackup,GoogleAppIndexingWarning,MissingApplicationIcon">
<activity
android:name=".MainFragmentActivity"
@@ -32,12 +33,6 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- <activity
- android:name=".doubleTransitionBug.DoubleTransitionBugActivity"
- android:exported="false" />
- <activity
- android:name=".kittenfragmenttransitions.KittenTransitionMainActivity"
- android:exported="false" />
</application>
</manifest>
diff --git a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt b/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimatorTestsFragment.kt
similarity index 61%
copy from fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
copy to fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimatorTestsFragment.kt
index 9c66c18..efddd99 100644
--- a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
+++ b/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimatorTestsFragment.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -16,6 +16,13 @@
package androidx.fragment.testapp
+import android.os.Bundle
+import android.view.View
import androidx.fragment.app.Fragment
+import androidx.fragment.testapp.basicAnimators.BasicFragmentAnimatorFragment
-class AnimationTestsFragment : Fragment(R.layout.animation_fragment)
+class AnimatorTestsFragment : Fragment(R.layout.animation_fragment) {
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ addButton("Basic Fragment Animators", BasicFragmentAnimatorFragment())
+ }
+}
diff --git a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/TestTypeSelectFragment.kt b/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/TestTypeSelectFragment.kt
index e7a7a5b..9f5c5f2 100644
--- a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/TestTypeSelectFragment.kt
+++ b/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/TestTypeSelectFragment.kt
@@ -28,25 +28,25 @@
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- addButton("Fragment Animation Test", AnimationTestsFragment())
+ addButton("Fragment Animator Test", AnimatorTestsFragment())
addButton("Fragment Transition Test", TransitionTestsFragment())
}
+}
- private fun Fragment.addButton(text: String, fragment: Fragment) {
- (requireView() as LinearLayout).addView(
- Button(context).apply {
- this.text = text
+internal fun Fragment.addButton(text: String, fragment: Fragment) {
+ (requireView() as LinearLayout).addView(
+ Button(context).apply {
+ this.text = text
- setOnClickListener {
- parentFragmentManager.commit {
- replace(R.id.fragment_container, fragment)
- addToBackStack(null)
- }
- }
- layoutParams = LinearLayout.LayoutParams(-1, 0).apply {
- weight = 1f
+ setOnClickListener {
+ parentFragmentManager.commit {
+ replace(R.id.fragment_container, fragment)
+ addToBackStack(null)
}
}
- )
- }
+ layoutParams = LinearLayout.LayoutParams(-1, 0).apply {
+ weight = 1f
+ }
+ }
+ )
}
diff --git a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/TransitionTestsFragment.kt b/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/TransitionTestsFragment.kt
index 5c898b3..5c65086 100644
--- a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/TransitionTestsFragment.kt
+++ b/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/TransitionTestsFragment.kt
@@ -23,14 +23,14 @@
import android.widget.LinearLayout
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
-import androidx.fragment.testapp.doubleTransitionBug.DoubleTransitionBugActivity
-import androidx.fragment.testapp.kittenfragmenttransitions.KittenTransitionMainActivity
+import androidx.fragment.testapp.doubleTransitionBug.DoubleTransitionBugFragment
+import androidx.fragment.testapp.kittenfragmenttransitions.KittenTransitionMainFragment
class TransitionTestsFragment : Fragment(R.layout.transition_fragment) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- addButton("Double Transition Bug", DoubleTransitionBugActivity::class.java)
- addButton("Kitten Transition", KittenTransitionMainActivity::class.java)
+ addButton("Double Transition Bug", DoubleTransitionBugFragment())
+ addButton("Kitten Transition", KittenTransitionMainFragment())
}
}
diff --git a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/basicAnimators/BasicFragmentAnimatorFragment.kt b/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/basicAnimators/BasicFragmentAnimatorFragment.kt
new file mode 100644
index 0000000..10cfa82
--- /dev/null
+++ b/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/basicAnimators/BasicFragmentAnimatorFragment.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.fragment.testapp.basicAnimators
+
+import android.graphics.Color
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import androidx.annotation.ColorInt
+import androidx.core.os.bundleOf
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.commit
+import androidx.fragment.testapp.R
+
+class BasicFragmentAnimatorFragment : Fragment(R.layout.basic_animators_main) {
+ private var count = 0
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ if (savedInstanceState == null) {
+ parentFragmentManager.beginTransaction()
+ .setPrimaryNavigationFragment(this)
+ .commit()
+ childFragmentManager.commit {
+ add(R.id.content, Fragment(R.layout.basic_animator_fragment))
+ }
+ }
+
+ return super.onCreateView(inflater, container, savedInstanceState)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ view.findViewById<Button>(R.id.next_button).setOnClickListener {
+ switchFragment()
+ }
+ }
+
+ private fun switchFragment() {
+ val fragment = MainFragment()
+ fragment.arguments = bundleOf("myarg" to when (count % 6) {
+ 1 -> { Color.GREEN }
+ 2 -> { Color.RED }
+ 3 -> { Color.YELLOW }
+ 4 -> { Color.GRAY }
+ 5 -> { Color.MAGENTA }
+ else -> {
+ Color.BLUE
+ }
+ })
+
+ count++
+
+ childFragmentManager.beginTransaction()
+ .setCustomAnimations(
+ androidx.fragment.R.animator.fragment_close_enter,
+ androidx.fragment.R.animator.fragment_close_exit,
+ androidx.fragment.R.animator.fragment_close_enter,
+ androidx.fragment.R.animator.fragment_close_exit,
+ )
+ .setReorderingAllowed(true)
+ .replace(R.id.content, fragment)
+ .addToBackStack(null)
+ .commit()
+ }
+
+ class MainFragment : Fragment(R.layout.basic_animator_fragment) {
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ @ColorInt val myarg = arguments?.getInt("myarg") ?: Color.RED
+
+ view.setBackgroundColor(myarg)
+ }
+ }
+}
\ No newline at end of file
diff --git a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/doubleTransitionBug/DoubleTransitionBugActivity.kt b/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/doubleTransitionBug/DoubleTransitionBugFragment.kt
similarity index 78%
rename from fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/doubleTransitionBug/DoubleTransitionBugActivity.kt
rename to fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/doubleTransitionBug/DoubleTransitionBugFragment.kt
index 969f8e7..a239452 100644
--- a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/doubleTransitionBug/DoubleTransitionBugActivity.kt
+++ b/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/doubleTransitionBug/DoubleTransitionBugFragment.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -20,28 +20,29 @@
import android.view.Gravity
import android.widget.Button
import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.commit
import androidx.fragment.testapp.R
import androidx.transition.Fade
import androidx.transition.Slide
-class DoubleTransitionBugActivity : FragmentActivity(R.layout.double_transition_bug_activity_main) {
+class DoubleTransitionBugFragment : Fragment(R.layout.double_transition_bug_activity_main) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
- supportFragmentManager.commit {
+ childFragmentManager.commit {
add(R.id.content, Fragment(R.layout.double_transition_bug_fragment_second))
}
}
- findViewById<Button>(R.id.important_button).setOnClickListener { switchFragment() }
+ requireActivity().findViewById<Button>(R.id.important_button).setOnClickListener {
+ switchFragment()
+ }
}
private fun switchFragment() {
- val currentFragment = supportFragmentManager.findFragmentById(R.id.content)
+ val currentFragment = childFragmentManager.findFragmentById(R.id.content)
currentFragment!!.exitTransition = Fade()
@@ -51,7 +52,7 @@
val first = Fragment(R.layout.double_transition_bug_fragment_first)
first.enterTransition = Fade().setDuration(5000)
- supportFragmentManager.beginTransaction()
+ childFragmentManager.beginTransaction()
.setReorderingAllowed(true)
.add(R.id.content, first)
.add(R.id.content, second)
diff --git a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/kittenfragmenttransitions/KittenTransitionMainActivity.kt b/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/kittenfragmenttransitions/KittenTransitionMainFragment.kt
similarity index 79%
rename from fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/kittenfragmenttransitions/KittenTransitionMainActivity.kt
rename to fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/kittenfragmenttransitions/KittenTransitionMainFragment.kt
index bd99b1f..13dc5a3 100644
--- a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/kittenfragmenttransitions/KittenTransitionMainActivity.kt
+++ b/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/kittenfragmenttransitions/KittenTransitionMainFragment.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -15,10 +15,10 @@
*/
package androidx.fragment.testapp.kittenfragmenttransitions
-import androidx.fragment.app.FragmentActivity
+import androidx.fragment.app.Fragment
import androidx.fragment.testapp.R
/**
* Main activity that holds our fragments
*/
-class KittenTransitionMainActivity : FragmentActivity(R.layout.kitten_activity_main)
+class KittenTransitionMainFragment : Fragment(R.layout.kitten_activity_main)
diff --git a/fragment/integration-tests/testapp/src/main/res/layouts/basicAnimators/layout/basic_animator_fragment.xml b/fragment/integration-tests/testapp/src/main/res/layouts/basicAnimators/layout/basic_animator_fragment.xml
new file mode 100644
index 0000000..fca3ed7
--- /dev/null
+++ b/fragment/integration-tests/testapp/src/main/res/layouts/basicAnimators/layout/basic_animator_fragment.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <FrameLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/fragment/integration-tests/testapp/src/main/res/layouts/basicAnimators/layout/basic_animators_main.xml b/fragment/integration-tests/testapp/src/main/res/layouts/basicAnimators/layout/basic_animators_main.xml
new file mode 100644
index 0000000..a136643
--- /dev/null
+++ b/fragment/integration-tests/testapp/src/main/res/layouts/basicAnimators/layout/basic_animators_main.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <FrameLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <Button
+ android:id="@+id/next_button"
+ android:layout_gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Next" />
+
+</FrameLayout>
diff --git a/glance/glance-appwidget/build.gradle b/glance/glance-appwidget/build.gradle
index 158c764..85a163f 100644
--- a/glance/glance-appwidget/build.gradle
+++ b/glance/glance-appwidget/build.gradle
@@ -70,7 +70,6 @@
androidTestImplementation(project(":test:screenshot:screenshot"))
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
- androidTestImplementation("androidx.room:room-runtime:2.4.3")
androidTestImplementation('androidx.core:core-ktx:1.7.0')
androidTestImplementation("androidx.work:work-testing:2.7.1")
androidTestImplementation(libs.espressoCore)
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt
index 01b903d..2430d3f 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt
@@ -36,7 +36,6 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import androidx.work.WorkManager
-import androidx.work.impl.WorkManagerImpl
import androidx.work.testing.WorkManagerTestInitHelper
import com.google.common.truth.Truth.assertThat
import java.lang.ref.WeakReference
@@ -112,8 +111,6 @@
mUiAutomation.dropShellPermissionIdentity()
}
WorkManager.getInstance(mContext).cancelAllWork()
- // TODO(b/242026176): remove this once WorkManager allows closing the test database.
- WorkManagerImpl.getInstance(context).workDatabase.close()
}
}
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/StrictModeTest.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/StrictModeTest.kt
index a4ccfdc..6a3e51f 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/StrictModeTest.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/StrictModeTest.kt
@@ -63,8 +63,10 @@
StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyListener(executor) {
+ Log.e("StrictModeTest", "Logging violation:")
+ Log.e("StrictModeTest", "$it")
+ Log.e("StrictModeTest", "Stack trace: ${it.stackTrace}", it.cause)
fail("Received violation: $it")
- Log.e("WIDGET", "$it")
}.build()
)
}
@@ -166,4 +168,4 @@
Truth.assertThat(CallbackTest.latch.await(5, TimeUnit.SECONDS)).isTrue()
Truth.assertThat(CallbackTest.received.get()).containsExactly(1, 2)
}
-}
+}
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index e655b07..2aacccb 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -42,7 +42,7 @@
kotlinCompileTesting = "1.4.9"
kotlinCoroutines = "1.6.4"
kotlinSerialization = "1.3.3"
-ksp = "1.8.20-1.0.10"
+ksp = "1.8.20-1.0.11"
ktlint = "0.46.0-20220520.192227-74"
leakcanary = "2.8.1"
media3 = "1.0.0-beta03"
@@ -223,11 +223,11 @@
okio = { module = "com.squareup.okio:okio", version = "3.1.0" }
playFeatureDelivery = { module = "com.google.android.play:feature-delivery", version = "2.0.1" }
playCore = { module = "com.google.android.play:core", version = "1.10.3" }
-playServicesAuth = {module = "com.google.android.gms:play-services-auth", version = "20.4.0"}
+playServicesAuth = {module = "com.google.android.gms:play-services-auth", version = "20.5.0"}
playServicesBase = { module = "com.google.android.gms:play-services-base", version = "17.0.0" }
playServicesBasement = { module = "com.google.android.gms:play-services-basement", version = "17.0.0" }
playServicesDevicePerformance = { module = "com.google.android.gms:play-services-deviceperformance", version = "16.0.0" }
-playServicesFido = {module = "com.google.android.gms:play-services-fido", version = "19.0.0"}
+playServicesFido = {module = "com.google.android.gms:play-services-fido", version = "20.0.1"}
playServicesWearable = { module = "com.google.android.gms:play-services-wearable", version = "17.1.0" }
paparazzi = { module = "app.cash.paparazzi:paparazzi", version.ref = "paparazzi" }
paparazziNativeJvm = { module = "app.cash.paparazzi:layoutlib-native-jdk11", version.ref = "paparazziNative" }
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV29Test.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV29Test.kt
index 5ecc43e..60cf00d 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV29Test.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV29Test.kt
@@ -41,7 +41,6 @@
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
@@ -229,7 +228,11 @@
bufferReadyLatch.countDown()
drawCancelledRequestLatch?.countDown()
}
- })
+ }).apply {
+ // See: b/236394768 Workaround for ANGLE issue where FBOs with HardwareBuffer
+ // attachments are not executed until a glReadPixels call is made
+ forceFlush.set(true)
+ }
try {
renderer.render(Color.RED)
assertTrue(initialDrawLatch.await(3000, TimeUnit.MILLISECONDS))
@@ -280,7 +283,11 @@
) {
// NO-OP
}
- })
+ }).apply {
+ // See: b/236394768 Workaround for ANGLE issue where FBOs with HardwareBuffer
+ // attachments are not executed until a glReadPixels call is made
+ forceFlush.set(true)
+ }
try {
val latch = CountDownLatch(1)
renderer.release(true) {
@@ -330,7 +337,11 @@
syncFenceCompat?.awaitForever()
drawLatch?.countDown()
}
- })
+ }).apply {
+ // See: b/236394768 Workaround for ANGLE issue where FBOs with HardwareBuffer
+ // attachments are not executed until a glReadPixels call is made
+ forceFlush.set(true)
+ }
try {
renderer.isVisible = false
drawLatch = CountDownLatch(1)
@@ -353,7 +364,6 @@
}
}
- @Ignore("b/274099885")
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
@Test
fun testBatchedRenders() {
@@ -378,7 +388,11 @@
) {
// NO-OP
}
- })
+ }).apply {
+ // See: b/236394768 Workaround for ANGLE issue where FBOs with HardwareBuffer
+ // attachments are not executed until a glReadPixels call is made
+ forceFlush.set(true)
+ }
try {
renderer.render(Color.RED)
renderer.render(Color.BLUE)
@@ -431,7 +445,11 @@
buffer = hardwareBuffer
renderLatch.countDown()
}
- })
+ }).apply {
+ // See: b/236394768 Workaround for ANGLE issue where FBOs with HardwareBuffer
+ // attachments are not executed until a glReadPixels call is made
+ forceFlush.set(true)
+ }
try {
renderer.render(0)
assertTrue(renderLatch.await(3000, TimeUnit.MILLISECONDS))
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/opengl/GLRendererTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/opengl/GLRendererTest.kt
index a03d42d..43f1de6 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/opengl/GLRendererTest.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/opengl/GLRendererTest.kt
@@ -987,6 +987,8 @@
width.toFloat(),
height.toFloat()
)
+ // See: b/236394768 Workaround for ANGLE issue where FBOs with HardwareBuffer
+ GLES20.glFinish()
supportsFence = eglManager.supportsNativeAndroidFence()
quadRenderer.release()
surface.release()
@@ -1125,6 +1127,8 @@
width.toFloat(),
height.toFloat()
)
+ // See: b/236394768 Workaround for ANGLE issue where FBOs with HardwareBuffer
+ GLES20.glFinish()
supportsFence = eglManager.supportsNativeAndroidFence()
quadRenderer.release()
deleteTexture(texId)
diff --git a/graphics/graphics-shapes/api/current.txt b/graphics/graphics-shapes/api/current.txt
index e7f488c..e4a534d 100644
--- a/graphics/graphics-shapes/api/current.txt
+++ b/graphics/graphics-shapes/api/current.txt
@@ -82,7 +82,7 @@
public final class RoundedPolygon {
ctor public RoundedPolygon(java.util.List<? extends android.graphics.PointF> vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF? center);
- ctor public RoundedPolygon(int numVertices, optional float radius, optional android.graphics.PointF center, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
+ ctor public RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional android.graphics.PointF center, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
ctor public RoundedPolygon(androidx.graphics.shapes.RoundedPolygon source);
method public android.graphics.RectF getBounds();
method public android.graphics.PointF getCenter();
@@ -103,9 +103,11 @@
}
public final class ShapesKt {
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float radius, optional android.graphics.PointF center);
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float radius);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius, optional android.graphics.PointF center);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices);
method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion);
+ method public static androidx.graphics.shapes.RoundedPolygon rectangle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF center);
method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF center);
method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding);
diff --git a/graphics/graphics-shapes/api/public_plus_experimental_current.txt b/graphics/graphics-shapes/api/public_plus_experimental_current.txt
index e7f488c..e4a534d 100644
--- a/graphics/graphics-shapes/api/public_plus_experimental_current.txt
+++ b/graphics/graphics-shapes/api/public_plus_experimental_current.txt
@@ -82,7 +82,7 @@
public final class RoundedPolygon {
ctor public RoundedPolygon(java.util.List<? extends android.graphics.PointF> vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF? center);
- ctor public RoundedPolygon(int numVertices, optional float radius, optional android.graphics.PointF center, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
+ ctor public RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional android.graphics.PointF center, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
ctor public RoundedPolygon(androidx.graphics.shapes.RoundedPolygon source);
method public android.graphics.RectF getBounds();
method public android.graphics.PointF getCenter();
@@ -103,9 +103,11 @@
}
public final class ShapesKt {
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float radius, optional android.graphics.PointF center);
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float radius);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius, optional android.graphics.PointF center);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices);
method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion);
+ method public static androidx.graphics.shapes.RoundedPolygon rectangle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF center);
method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF center);
method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding);
diff --git a/graphics/graphics-shapes/api/restricted_current.txt b/graphics/graphics-shapes/api/restricted_current.txt
index e7f488c..e4a534d 100644
--- a/graphics/graphics-shapes/api/restricted_current.txt
+++ b/graphics/graphics-shapes/api/restricted_current.txt
@@ -82,7 +82,7 @@
public final class RoundedPolygon {
ctor public RoundedPolygon(java.util.List<? extends android.graphics.PointF> vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF? center);
- ctor public RoundedPolygon(int numVertices, optional float radius, optional android.graphics.PointF center, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
+ ctor public RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional android.graphics.PointF center, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
ctor public RoundedPolygon(androidx.graphics.shapes.RoundedPolygon source);
method public android.graphics.RectF getBounds();
method public android.graphics.PointF getCenter();
@@ -103,9 +103,11 @@
}
public final class ShapesKt {
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float radius, optional android.graphics.PointF center);
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float radius);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius, optional android.graphics.PointF center);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices);
method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion);
+ method public static androidx.graphics.shapes.RoundedPolygon rectangle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF center);
method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional android.graphics.PointF center);
method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding);
diff --git a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/RoundedPolygonTest.kt b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/RoundedPolygonTest.kt
index 918f919..eabbc8d 100644
--- a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/RoundedPolygonTest.kt
+++ b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/RoundedPolygonTest.kt
@@ -20,6 +20,7 @@
import androidx.core.graphics.plus
import androidx.core.graphics.times
import androidx.test.filters.SmallTest
+import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Test
@@ -31,6 +32,10 @@
@Test
fun numVertsConstructorTest() {
+ Assert.assertThrows(IllegalArgumentException::class.java) {
+ RoundedPolygon(2)
+ }
+
val square = RoundedPolygon(4)
var min = PointF(-1f, -1f)
var max = PointF(1f, 1f)
@@ -58,6 +63,11 @@
val p1 = PointF(0f, 1f)
val p2 = PointF(-1f, 0f)
val p3 = PointF(0f, -1f)
+
+ Assert.assertThrows(IllegalArgumentException::class.java) {
+ RoundedPolygon(listOf(p0, p1))
+ }
+
val manualSquare = RoundedPolygon(listOf(p0, p1, p2, p3))
var min = PointF(-1f, -1f)
var max = PointF(1f, 1f)
@@ -109,4 +119,107 @@
assertEqualish(0.5f, lowerEdge.p3.x)
assertEqualish(0.0f, lowerEdge.p3.y)
}
-}
\ No newline at end of file
+
+ /*
+ * In the following tests, we check how much was cut for the top left (vertex 0) and bottom
+ * left corner (vertex 3).
+ * In particular, both vertex are competing for space in the left side.
+ *
+ * Vertex 0 Vertex 1
+ * *---------------------*
+ * | |
+ * *---------------------*
+ * Vertex 3 Vertex 2
+ */
+ private val points = 20
+
+ @Test
+ fun unevenSmoothingTest() {
+ // Vertex 3 has the default 0.5 radius, 0 smoothing.
+ // Vertex 0 has 0.4 radius, and smoothing varying from 0 to 1.
+ repeat(points + 1) {
+ val smooth = it.toFloat() / points
+ doUnevenSmoothTest(
+ CornerRounding(0.4f, smooth),
+ expectedV0SX = 0.4f * (1 + smooth),
+ expectedV0SY = (0.4f * (1 + smooth)).coerceAtMost(0.5f),
+ expectedV3SY = 0.5f,
+ )
+ }
+ }
+
+ @Test
+ fun unevenSmoothingTest2() {
+ // Vertex 3 has 0.2f radius and 0.2f smoothing, so it takes at most 0.4f
+ // Vertex 0 has 0.4f radius and smoothing varies from 0 to 1, when it reaches 0.5 it starts
+ // competing with vertex 3 for space.
+ repeat(points + 1) {
+ val smooth = it.toFloat() / points
+
+ val smoothWantedV0 = 0.4f * smooth
+ val smoothWantedV3 = 0.2f
+
+ // There is 0.4f room for smoothing
+ val factor = (0.4f / (smoothWantedV0 + smoothWantedV3)).coerceAtMost(1f)
+ doUnevenSmoothTest(
+ CornerRounding(0.4f, smooth),
+ expectedV0SX = 0.4f * (1 + smooth),
+ expectedV0SY = 0.4f + factor * smoothWantedV0,
+ expectedV3SY = 0.2f + factor * smoothWantedV3,
+ rounding3 = CornerRounding(0.2f, 1f)
+ )
+ }
+ }
+
+ @Test
+ fun unevenSmoothingTest3() {
+ // Vertex 3 has 0.6f radius.
+ // Vertex 0 has 0.4f radius and smoothing varies from 0 to 1. There is no room for smoothing
+ // on the segment between these vertices, but vertex 0 can still have smoothing on the top
+ // side.
+ repeat(points + 1) {
+ val smooth = it.toFloat() / points
+
+ doUnevenSmoothTest(
+ CornerRounding(0.4f, smooth),
+ expectedV0SX = 0.4f * (1 + smooth),
+ expectedV0SY = 0.4f,
+ expectedV3SY = 0.6f,
+ rounding3 = CornerRounding(0.6f)
+ )
+ }
+ }
+
+ private fun doUnevenSmoothTest(
+ // Corner rounding parameter for vertex 0 (top left)
+ rounding0: CornerRounding,
+ expectedV0SX: Float, // Expected total cut from vertex 0 towards vertex 1
+ expectedV0SY: Float, // Expected total cut from vertex 0 towards vertex 3
+ expectedV3SY: Float, // Expected total cut from vertex 3 towards vertex 0
+ // Corner rounding parameter for vertex 3 (bottom left)
+ rounding3: CornerRounding = CornerRounding(0.5f)
+ ) {
+ val p0 = PointF(0f, 0f)
+ val p1 = PointF(5f, 0f)
+ val p2 = PointF(5f, 1f)
+ val p3 = PointF(0f, 1f)
+
+ val pvRounding = listOf(
+ rounding0,
+ CornerRounding.Unrounded,
+ CornerRounding.Unrounded,
+ rounding3,
+ )
+ val polygon = RoundedPolygon(
+ vertices = listOf(p0, p1, p2, p3),
+ perVertexRounding = pvRounding
+ )
+ val (e01, _, _, e30) = polygon.features.filterIsInstance<RoundedPolygon.Edge>()
+ val msg = "r0 = ${show(rounding0)}, r3 = ${show(rounding3)}"
+ assertEqualish(expectedV0SX, e01.cubics.first().p0.x, msg)
+ assertEqualish(expectedV0SY, e30.cubics.first().p3.y, msg)
+ assertEqualish(expectedV3SY, 1f - e30.cubics.first().p0.y, msg)
+ }
+
+ private fun show(cr: CornerRounding) = "(r=${cr.radius}, s=${cr.smoothing})"
+}
diff --git a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/ShapesTest.kt b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/ShapesTest.kt
index 4a115d7..abd569c 100644
--- a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/ShapesTest.kt
+++ b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/ShapesTest.kt
@@ -21,6 +21,7 @@
import androidx.test.filters.SmallTest
import kotlin.AssertionError
import kotlin.math.sqrt
+import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThrows
import org.junit.Test
@@ -90,9 +91,19 @@
@Test
fun circleTest() {
+ Assert.assertThrows(IllegalArgumentException::class.java) {
+ RoundedPolygon.circle(2)
+ }
+
val circle = RoundedPolygon.circle()
assertCircleShape(circle.toCubicShape())
+ val simpleCircle = RoundedPolygon.circle(3)
+ assertCircleShape(simpleCircle.toCubicShape())
+
+ val complexCircle = RoundedPolygon.circle(20)
+ assertCircleShape(complexCircle.toCubicShape())
+
val bigCircle = RoundedPolygon.circle(radius = 3f)
assertCircleShape(bigCircle.toCubicShape(), radius = 3f)
diff --git a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/TestUtils.kt b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/TestUtils.kt
index d447fdf..31d0440 100644
--- a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/TestUtils.kt
+++ b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/TestUtils.kt
@@ -45,8 +45,8 @@
assertTrue(actual.y <= expected.y + Epsilon)
}
-fun assertEqualish(expected: Float, actual: Float) {
- assertEquals(expected, actual, Epsilon)
+fun assertEqualish(expected: Float, actual: Float, message: String? = null) {
+ assertEquals(message ?: "", expected, actual, Epsilon)
}
fun assertInBounds(shape: CubicShape, minPoint: PointF, maxPoint: PointF) {
diff --git a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/CubicShape.kt b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/CubicShape.kt
index 6d872cf..90f1cfa 100644
--- a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/CubicShape.kt
+++ b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/CubicShape.kt
@@ -81,7 +81,7 @@
/**
* Transforms (scales, rotates, and translates) the shape by the given matrix.
* Note that this operation alters the points in the shape directly; the original
- * points are not retained, nor is the matrix itself. This calling this function
+ * points are not retained, nor is the matrix itself. Thus calling this function
* twice with the same matrix will composite the effect. For example, a matrix which
* scales by 2 will scale the shape by 2. Calling transform twice with that matrix
* will have the effect os scaling the shape size by 4.
diff --git a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/RoundedPolygon.kt b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/RoundedPolygon.kt
index 17c1fdd..520b787 100644
--- a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/RoundedPolygon.kt
+++ b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/RoundedPolygon.kt
@@ -22,6 +22,7 @@
import android.graphics.Path
import android.graphics.PointF
import android.graphics.RectF
+import androidx.annotation.IntRange
import androidx.core.graphics.div
import androidx.core.graphics.minus
import androidx.core.graphics.plus
@@ -80,8 +81,8 @@
* Constructs a RoundedPolygon object from a given list of vertices, with optional
* corner-rounding parameters for all corners or per-corner.
*
- * A RoundedPolygon without any rounding parameters is equivalent to a [RoundedPolygon] constructed
- * with the same [vertices] and [center].
+ * A RoundedPolygon without any rounding parameters is equivalent to a [RoundedPolygon]
+ * constructed with the same [vertices] and [center].
*
* @param vertices The list of vertices in this polygon. This should be an ordered list
* (with the outline of the shape going from each vertex to the next in order of this
@@ -99,6 +100,7 @@
*
* @throws IllegalArgumentException If [perVertexRounding] is not null, it must be
* the same size as the [vertices] list.
+ * @throws IllegalArgumentException [vertices] must have a size of at least three.
*/
constructor(
vertices: List<PointF>,
@@ -113,7 +115,11 @@
/**
* This constructor takes the number of vertices in the resulting polygon. These vertices are
* positioned on a virtual circle around a given center with each vertex positioned [radius]
- * distance from that center, equally spaced (with equal angles between them).
+ * distance from that center, equally spaced (with equal angles between them). If no radius
+ * is supplied, the shape will be created with a default radius of 1, resulting in a shape
+ * whose vertices lie on a unit circle, with width/height of 2. That default polygon will
+ * probably need to be rescaled using [transform] into the appropriate size for the UI in
+ * which it will be drawn.
*
* The [rounding] and [perVertexRounding] parameters are optional. If not supplied, the result
* will be a regular polygon with straight edges and unrounded corners.
@@ -135,9 +141,10 @@
*
* @throws IllegalArgumentException If [perVertexRounding] is not null, it must have
* [numVertices] elements.
+ * @throws IllegalArgumentException [numVertices] must be at least 3.
*/
constructor(
- numVertices: Int,
+ @IntRange(from = 3) numVertices: Int,
radius: Float = 1f,
center: PointF = PointF(0f, 0f),
rounding: CornerRounding = CornerRounding.Unrounded,
@@ -189,6 +196,9 @@
rounding: CornerRounding = CornerRounding.Unrounded,
perVertexRounding: List<CornerRounding>? = null
) {
+ if (vertices.size < 3) {
+ throw IllegalArgumentException("Polygons must have at least 3 vertices")
+ }
if (perVertexRounding != null && perVertexRounding.size != vertices.size) {
throw IllegalArgumentException("perVertexRounding list should be either null or " +
"the same size as the vertices list")
@@ -228,13 +238,12 @@
sideSize / expectedRoundCut to 0f
} else if (expectedCut > sideSize) {
// We can do full rounding, but not full smoothing.
- 1f to (sideSize - expectedRoundCut) / expectedCut
+ 1f to (sideSize - expectedRoundCut) / (expectedCut - expectedRoundCut)
} else {
// There is enough room for rounding & smoothing.
- 0f to 1f
+ 1f to 1f
}
}
-
// Create and store list of beziers for each [potentially] rounded corner
for (i in 0 until n) {
// allowedCuts[0] is for the side from the previous corner to this one,
@@ -242,7 +251,7 @@
val allowedCuts = (0..1).map { delta ->
val (roundCutRatio, cutRatio) = cutAdjusts[(i + n - 1 + delta) % n]
roundedCorners[i].expectedRoundCut * roundCutRatio +
- roundedCorners[i].expectedCut * cutRatio
+ (roundedCorners[i].expectedCut - roundedCorners[i].expectedRoundCut) * cutRatio
}
corners.add(
roundedCorners[i].getCubics(
@@ -274,7 +283,22 @@
cubicShape.updateCubics(cubics)
}
- // Transforms as usual, plus the polygon's center
+ /**
+ * Transforms (scales, rotates, and translates) the polygon by the given matrix.
+ * Note that this operation alters the points in the polygon directly; the original
+ * points are not retained, nor is the matrix itself. Thus calling this function
+ * twice with the same matrix will composite the effect. For example, a matrix which
+ * scales by 2 will scale the polygon by 2. Calling transform twice with that matrix
+ * will have the effect os scaling the shape size by 4.
+ *
+ * Note that [RoundedPolygon] objects created with default radius and center values will
+ * probably need to be scaled and repositioned using [transform] to be displayed correctly
+ * in the UI. Polygons are created by default on the unit circle around a center
+ * of (0, 0), so the resulting geometry has a bounding box width and height of 2x2; It should
+ * be resized to fit where it will be displayed appropriately.
+ *
+ * @param matrix The matrix used to transform the polygon
+ */
fun transform(matrix: Matrix) {
cubicShape.transform(matrix)
val point = scratchTransformPoint
diff --git a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Shapes.kt b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Shapes.kt
index 12bfeeb..7c4d3ec 100644
--- a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Shapes.kt
+++ b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Shapes.kt
@@ -17,20 +17,79 @@
package androidx.graphics.shapes
import android.graphics.PointF
-
-private const val Sqrt2 = 1.41421356f
+import androidx.annotation.IntRange
+import kotlin.math.cos
/**
- * Circle creates a square polygon shape whose four corners are rounded with circular
- * arcs.
+ * Creates a circular shape, approximating the rounding of the shape around the underlying
+ * polygon vertices.
*
- * @param radius optional radius for the circle, default value is 1.0.
+ * @param numVertices The number of vertices in the underlying polygon with which to
+ * approximate the circle, default value is 8
+ * @param radius optional radius for the circle, default value is 1.0
* @param center optional center for the circle, default value is (0, 0)
+ *
+ * @throws IllegalArgumentException [numVertices] must be at least 3
*/
@JvmOverloads
-fun RoundedPolygon.Companion.circle(radius: Float = 1f, center: PointF = Zero): RoundedPolygon {
- return RoundedPolygon(4, rounding = CornerRounding(radius), radius = radius * Sqrt2,
- center = center)
+fun RoundedPolygon.Companion.circle(
+ @IntRange(from = 3) numVertices: Int = 8,
+ radius: Float = 1f,
+ center: PointF = Zero
+): RoundedPolygon {
+
+ if (numVertices < 3) throw IllegalArgumentException("Circle must have at least three vertices")
+
+ // Half of the angle between two adjacent vertices on the polygon
+ val theta = FloatPi / numVertices
+ // Radius of the underlying RoundedPolygon object given the desired radius of the circle
+ val polygonRadius = radius / cos(theta)
+ return RoundedPolygon(
+ numVertices, rounding = CornerRounding(radius), radius = polygonRadius,
+ center = center
+ )
+}
+
+/**
+ * Creates a rectangular shape with the given width/height around the given center.
+ * Optional rounding parameters can be used to create a rounded rectangle instead.
+ *
+ * As with all [RoundedPolygon] objects, if this shape is created with default dimensions and
+ * center, it is sized to fit within the 2x2 bounding box around a center of (0, 0) and will
+ * need to be scaled and moved using [RoundedPolygon.transform] to fit the intended area
+ * in a UI.
+ *
+ * @param width The width of the rectangle, default value is 2
+ * @param height The height of the rectangle, default value is 2
+ * @param rounding The [CornerRounding] properties of every vertex. If some vertices should
+ * have different rounding properties, then use [perVertexRounding] instead. The default
+ * rounding value is [CornerRounding.Unrounded], meaning that the polygon will use the vertices
+ * themselves in the final shape and not curves rounded around the vertices.
+ * @param perVertexRounding The [CornerRounding] properties of every vertex. If this
+ * parameter is not null, then it must be of size 4 for the four corners of the shape. If this
+ * parameter is null, then the polygon will use the [rounding] parameter for every vertex instead.
+ * The default value is null.
+ * @param center The center of the rectangle, around which all vertices will be placed
+ * equidistantly. The default center is at (0,0).
+ */
+fun RoundedPolygon.Companion.rectangle(
+ width: Float = 2f,
+ height: Float = 2f,
+ rounding: CornerRounding = CornerRounding.Unrounded,
+ perVertexRounding: List<CornerRounding>? = null,
+ center: PointF = Zero
+): RoundedPolygon {
+ val left = center.x - width / 2
+ val top = center.y - height / 2
+ val right = center.x + width / 2
+ val bottom = center.y + height / 2
+
+ return RoundedPolygon(
+ listOf(
+ PointF(right, bottom), PointF(left, bottom), PointF(left, top),
+ PointF(right, top)
+ ), rounding, perVertexRounding, center
+ )
}
/**
diff --git a/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/MainActivity.kt b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/MainActivity.kt
index 91a8db6..d49c55a 100644
--- a/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/MainActivity.kt
+++ b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/MainActivity.kt
@@ -175,9 +175,9 @@
// LINE 1
// Circle
ShapeParameters(
- sides = 4,
+ sides = 8,
roundness = 1f,
- shapeId = ShapeParameters.ShapeId.Polygon
+ shapeId = ShapeParameters.ShapeId.Circle
),
//
ShapeParameters(
@@ -230,7 +230,7 @@
rotation = 45f,
shapeId = ShapeParameters.ShapeId.Blob
),
- // Scalop
+ // Scallop
ShapeParameters(
sides = 12,
innerRadius = .928f,
@@ -250,11 +250,10 @@
ShapeParameters(roundness = .4f, shapeId = ShapeParameters.ShapeId.CornerSE),
// Non - material shapes:
- // Square
+ // Rectangle
ShapeParameters(
sides = 4,
- rotation = 45f,
- shapeId = ShapeParameters.ShapeId.Polygon
+ shapeId = ShapeParameters.ShapeId.Rectangle
),
// Pentagon
@@ -272,12 +271,13 @@
shapeId = ShapeParameters.ShapeId.Star
),
- // 8-Sided Star
+ // Round Rect
ShapeParameters(
- sides = 8,
- innerRadius = .6f,
- shapeId = ShapeParameters.ShapeId.Star
- )
+ sides = 4,
+ roundness = .5f,
+ smooth = 1f,
+ shapeId = ShapeParameters.ShapeId.Rectangle
+ ),
)
}
editing?.let {
diff --git a/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/ShapeEditor.kt b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/ShapeEditor.kt
index f4066b8..7df2d10 100644
--- a/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/ShapeEditor.kt
+++ b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/ShapeEditor.kt
@@ -48,9 +48,12 @@
import androidx.core.graphics.plus
import androidx.graphics.shapes.CornerRounding
import androidx.graphics.shapes.RoundedPolygon
+import androidx.graphics.shapes.circle
+import androidx.graphics.shapes.rectangle
import androidx.graphics.shapes.star
import kotlin.math.cos
import kotlin.math.max
+import kotlin.math.min
import kotlin.math.roundToInt
import kotlin.math.sin
@@ -105,7 +108,7 @@
)
enum class ShapeId {
- Star, Polygon, Triangle, Blob, CornerSE
+ Star, Polygon, Triangle, Blob, CornerSE, Circle, Rectangle
}
private fun radialToCartesian(
@@ -197,7 +200,7 @@
PointF(sx, sy),
PointF(-sx, sy),
),
- rounding = CornerRounding(this.roundness.value, this.smooth.value),
+ rounding = CornerRounding(min(sx, sy), this.smooth.value),
center = PointZero
)
},
@@ -235,6 +238,40 @@
usesSides = false,
usesInnerRatio = false,
usesInnerParameters = false
+ ),
+ ShapeItem(
+ "Circle", shapegen = {
+ RoundedPolygon.circle(this.sides.value.roundToInt())
+ },
+ debugDump = {
+ debugLog(
+ "ShapeParameters(roundness = ${this.roundness.value}f, " +
+ "smooth = ${this.smooth.value}f, " +
+ rotationAsString() +
+ "shapeId = ShapeParameters.ShapeId.Circle)"
+ )
+ },
+ usesSides = true,
+ usesInnerRatio = false,
+ usesInnerParameters = false
+ ),
+ ShapeItem(
+ "Rectangle", shapegen = {
+ RoundedPolygon.rectangle(width = 4f, height = 2f,
+ rounding = CornerRounding(this.roundness.value, this.smooth.value),
+ )
+ },
+ debugDump = {
+ debugLog(
+ "ShapeParameters(roundness = ${this.roundness.value}f, " +
+ "smooth = ${this.smooth.value}f, " +
+ rotationAsString() +
+ "shapeId = ShapeParameters.ShapeId.Rectangle)"
+ )
+ },
+ usesSides = false,
+ usesInnerRatio = false,
+ usesInnerParameters = false
)
/*
diff --git a/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeActivity.kt b/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeActivity.kt
index f70aa81..bff8b6b 100644
--- a/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeActivity.kt
+++ b/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeActivity.kt
@@ -28,6 +28,7 @@
import androidx.graphics.shapes.CornerRounding.Companion.Unrounded
import androidx.graphics.shapes.RoundedPolygon
import androidx.graphics.shapes.circle
+import androidx.graphics.shapes.rectangle
import androidx.graphics.shapes.star
class ShapeActivity : Activity() {
@@ -70,8 +71,8 @@
shapes.add(RoundedPolygon.circle())
// "Squirrel" to DefaultShapes.fgSquircle(0.9f),
shapes.add(RoundedPolygon(4))
- // "Squircle" to DefaultShapes.fgSquircle(0.7f),
- shapes.add(RoundedPolygon(4))
+ // Square, using rectangle function
+ shapes.add(RoundedPolygon.rectangle(width = 2f, height = 2f))
// "Scallop" to DefaultShapes.Scallop,
shapes.add(MaterialShapes.scallop())
// "Clover" to DefaultShapes.Clover,
@@ -79,8 +80,8 @@
// "Alice" to DefaultShapes.Alice,
shapes.add(MaterialShapes.alice())
- // "Veronica" to DefaultShapes.Alice.rotate(TwoPI / 2),
- shapes.add(RoundedPolygon(4))
+ // Rectangle
+ shapes.add(RoundedPolygon.rectangle(width = 4f, height = 2f))
// "Wiggle-Star" to DefaultShapes.WiggleStar,
shapes.add(MaterialShapes.wiggleStar())
// "Wovel" to DefaultShapes.Wovel,
@@ -92,12 +93,16 @@
shapes.add(blobR2)
// "More" to DefaultShapes.More,
shapes.add(MaterialShapes.more())
- // "Less" to DefaultShapes.More.rotate(TwoPI / 2),
- shapes.add(RoundedPolygon(4))
- // "CornerNE" to DefaultShapes.CornerSE.rotate(-TwoPI / 4),
- shapes.add(RoundedPolygon(4))
- // "CornerNW" to DefaultShapes.CornerSE.rotate(TwoPI / 2),
- shapes.add(RoundedPolygon(4))
+ // Round Rect
+ shapes.add(RoundedPolygon.rectangle(width = 4f, height = 2f,
+ rounding = CornerRounding(1f)
+ ))
+ // Round Rect (smoothed)
+ shapes.add(RoundedPolygon.rectangle(width = 4f, height = 2f,
+ rounding = CornerRounding(1f, .5f)))
+ // Round Rect (smoothed more)
+ shapes.add(RoundedPolygon.rectangle(width = 4f, height = 2f,
+ rounding = CornerRounding(1f, 1f)))
// "CornerSW" to DefaultShapes.CornerSE.rotate(TwoPI / 4),
shapes.add(RoundedPolygon(4))
diff --git a/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeView.kt b/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeView.kt
index e3cf5c9..176f118 100644
--- a/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeView.kt
+++ b/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeView.kt
@@ -41,6 +41,7 @@
val scaleFactor = min(scaleX, scaleY)
return scaleFactor
}
+
private fun calculateMatrix(bounds: RectF): Matrix {
val scale = calculateScale(bounds)
val scaledLeft = scale * bounds.left
diff --git a/hilt/hilt-navigation-compose/build.gradle b/hilt/hilt-navigation-compose/build.gradle
index 3cc6e95..86b72a7 100644
--- a/hilt/hilt-navigation-compose/build.gradle
+++ b/hilt/hilt-navigation-compose/build.gradle
@@ -39,7 +39,7 @@
api projectOrArtifact(":hilt:hilt-navigation")
api("androidx.compose.runtime:runtime:1.0.1")
api("androidx.compose.ui:ui:1.0.1")
- api(project(":lifecycle:lifecycle-viewmodel-compose"))
+ api("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
api("androidx.navigation:navigation-compose:2.5.1")
androidTestImplementation(libs.testExtJunit)
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java
index 00afeb8..a4432f0 100644
--- a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java
@@ -162,13 +162,23 @@
@Override
public void reportResult(String result) {
Objects.requireNonNull(result);
- handleEvaluationResult(mCompleter, result);
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ handleEvaluationResult(mCompleter, result);
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
}
@Override
public void reportError(@ExecutionErrorTypes int type, String error) {
Objects.requireNonNull(error);
- handleEvaluationError(mCompleter, type, error);
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ handleEvaluationError(mCompleter, type, error);
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
}
}
diff --git a/leanback/leanback-paging/build.gradle b/leanback/leanback-paging/build.gradle
index e0786f9..6c51eec 100644
--- a/leanback/leanback-paging/build.gradle
+++ b/leanback/leanback-paging/build.gradle
@@ -36,7 +36,7 @@
}
androidTestImplementation(libs.kotlinTest)
androidTestImplementation(libs.kotlinCoroutinesTest)
- androidTestImplementation("androidx.arch.core:core-testing:2.1.0")
+ androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.3.1")
}
diff --git a/lifecycle/lifecycle-extensions/build.gradle b/lifecycle/lifecycle-extensions/build.gradle
index a41fdaa..b729d31 100644
--- a/lifecycle/lifecycle-extensions/build.gradle
+++ b/lifecycle/lifecycle-extensions/build.gradle
@@ -25,8 +25,8 @@
dependencies {
api("androidx.lifecycle:lifecycle-runtime:2.2.0")
- api("androidx.arch.core:core-common:2.1.0")
- api("androidx.arch.core:core-runtime:2.1.0")
+ api("androidx.arch.core:core-common:2.2.0")
+ api("androidx.arch.core:core-runtime:2.2.0")
api("androidx.fragment:fragment:1.2.0")
api("androidx.lifecycle:lifecycle-common:2.2.0")
api("androidx.lifecycle:lifecycle-livedata:2.2.0")
@@ -34,7 +34,7 @@
api("androidx.lifecycle:lifecycle-service:2.2.0")
api("androidx.lifecycle:lifecycle-viewmodel:2.2.0")
- testImplementation("androidx.arch.core:core-testing:2.1.0")
+ testImplementation("androidx.arch.core:core-testing:2.2.0")
testImplementation(libs.junit)
testImplementation(libs.mockitoCore4)
diff --git a/lifecycle/lifecycle-livedata-core-ktx/build.gradle b/lifecycle/lifecycle-livedata-core-ktx/build.gradle
index c36dca9..6ab8c4e6 100644
--- a/lifecycle/lifecycle-livedata-core-ktx/build.gradle
+++ b/lifecycle/lifecycle-livedata-core-ktx/build.gradle
@@ -28,7 +28,7 @@
api(project(":lifecycle:lifecycle-livedata-core"))
api(libs.kotlinStdlib)
testImplementation(project(":lifecycle:lifecycle-runtime"))
- testImplementation("androidx.arch.core:core-testing:2.1.0")
+ testImplementation("androidx.arch.core:core-testing:2.2.0")
testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
testImplementation(libs.kotlinCoroutinesTest)
testImplementation(libs.junit)
diff --git a/lifecycle/lifecycle-livedata-core-truth/build.gradle b/lifecycle/lifecycle-livedata-core-truth/build.gradle
index 6f8a4c4..910f657 100644
--- a/lifecycle/lifecycle-livedata-core-truth/build.gradle
+++ b/lifecycle/lifecycle-livedata-core-truth/build.gradle
@@ -29,7 +29,7 @@
api(libs.kotlinStdlib)
testImplementation(libs.truth)
testImplementation(libs.mockitoCore4)
- testImplementation("androidx.arch.core:core-testing:2.1.0")
+ testImplementation("androidx.arch.core:core-testing:2.2.0")
testImplementation(project(":internal-testutils-truth"))
}
diff --git a/lifecycle/lifecycle-livedata-core/build.gradle b/lifecycle/lifecycle-livedata-core/build.gradle
index 929ec53..1947720d 100644
--- a/lifecycle/lifecycle-livedata-core/build.gradle
+++ b/lifecycle/lifecycle-livedata-core/build.gradle
@@ -24,12 +24,12 @@
dependencies {
api(libs.kotlinStdlib)
- implementation("androidx.arch.core:core-common:2.1.0")
- implementation("androidx.arch.core:core-runtime:2.1.0")
+ implementation("androidx.arch.core:core-common:2.2.0")
+ implementation("androidx.arch.core:core-runtime:2.2.0")
api(project(":lifecycle:lifecycle-common"))
testImplementation(project(":lifecycle:lifecycle-runtime"))
- testImplementation("androidx.arch.core:core-testing:2.1.0")
+ testImplementation("androidx.arch.core:core-testing:2.2.0")
testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
testImplementation(libs.kotlinCoroutinesTest)
testImplementation(libs.junit)
diff --git a/lifecycle/lifecycle-livedata-ktx/build.gradle b/lifecycle/lifecycle-livedata-ktx/build.gradle
index 97e3ebb..48f4b7b 100644
--- a/lifecycle/lifecycle-livedata-ktx/build.gradle
+++ b/lifecycle/lifecycle-livedata-ktx/build.gradle
@@ -30,7 +30,7 @@
api(libs.kotlinStdlib)
api(libs.kotlinCoroutinesCore)
testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
- testImplementation("androidx.arch.core:core-testing:2.1.0")
+ testImplementation("androidx.arch.core:core-testing:2.2.0")
testImplementation(libs.junit)
testImplementation(libs.truth)
testImplementation(libs.kotlinCoroutinesTest)
diff --git a/lifecycle/lifecycle-livedata/build.gradle b/lifecycle/lifecycle-livedata/build.gradle
index cfa330b..824c7f0 100644
--- a/lifecycle/lifecycle-livedata/build.gradle
+++ b/lifecycle/lifecycle-livedata/build.gradle
@@ -24,12 +24,12 @@
dependencies {
api(libs.kotlinStdlib)
- implementation("androidx.arch.core:core-common:2.1.0")
- api("androidx.arch.core:core-runtime:2.1.0")
+ implementation("androidx.arch.core:core-common:2.2.0")
+ api("androidx.arch.core:core-runtime:2.2.0")
api(project(":lifecycle:lifecycle-livedata-core"))
testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
- testImplementation("androidx.arch.core:core-testing:2.1.0")
+ testImplementation("androidx.arch.core:core-testing:2.2.0")
testImplementation(libs.kotlinCoroutinesTest)
testImplementation(libs.junit)
testImplementation(libs.mockitoCore4)
diff --git a/lifecycle/lifecycle-reactivestreams-ktx/build.gradle b/lifecycle/lifecycle-reactivestreams-ktx/build.gradle
index 587e146..79856f6 100644
--- a/lifecycle/lifecycle-reactivestreams-ktx/build.gradle
+++ b/lifecycle/lifecycle-reactivestreams-ktx/build.gradle
@@ -33,7 +33,7 @@
testImplementation(libs.truth)
testImplementation(libs.kotlinCoroutinesTest)
testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
- testImplementation("androidx.arch.core:core-testing:2.1.0")
+ testImplementation("androidx.arch.core:core-testing:2.2.0")
}
androidx {
diff --git a/lifecycle/lifecycle-reactivestreams/build.gradle b/lifecycle/lifecycle-reactivestreams/build.gradle
index 6730a57..e47bb63 100644
--- a/lifecycle/lifecycle-reactivestreams/build.gradle
+++ b/lifecycle/lifecycle-reactivestreams/build.gradle
@@ -25,7 +25,7 @@
dependencies {
api(libs.kotlinStdlib)
- api("androidx.arch.core:core-common:2.1.0")
+ api("androidx.arch.core:core-common:2.2.0")
api(project(":lifecycle:lifecycle-common"))
api(project(":lifecycle:lifecycle-livedata"))
api(project(":lifecycle:lifecycle-runtime"))
@@ -39,7 +39,7 @@
testImplementation(libs.truth)
testImplementation(libs.kotlinCoroutinesTest)
testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
- testImplementation("androidx.arch.core:core-testing:2.1.0")
+ testImplementation("androidx.arch.core:core-testing:2.2.0")
}
androidx {
diff --git a/lifecycle/lifecycle-runtime-compose/api/current.txt b/lifecycle/lifecycle-runtime-compose/api/current.txt
index 0686666..5b19f2b 100644
--- a/lifecycle/lifecycle-runtime-compose/api/current.txt
+++ b/lifecycle/lifecycle-runtime-compose/api/current.txt
@@ -8,6 +8,10 @@
method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, T initialValue, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
}
+ public final class LifecycleEffectKt {
+ method @androidx.compose.runtime.Composable public static void LifecycleEventEffect(androidx.lifecycle.Lifecycle.Event event, optional androidx.lifecycle.LifecycleOwner lifecycleOwner, kotlin.jvm.functions.Function0<kotlin.Unit> onEvent);
+ }
+
public final class LifecycleExtKt {
method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.lifecycle.Lifecycle.State> currentStateAsState(androidx.lifecycle.Lifecycle);
}
diff --git a/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt b/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt
index 0686666..5b19f2b 100644
--- a/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt
+++ b/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt
@@ -8,6 +8,10 @@
method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, T initialValue, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
}
+ public final class LifecycleEffectKt {
+ method @androidx.compose.runtime.Composable public static void LifecycleEventEffect(androidx.lifecycle.Lifecycle.Event event, optional androidx.lifecycle.LifecycleOwner lifecycleOwner, kotlin.jvm.functions.Function0<kotlin.Unit> onEvent);
+ }
+
public final class LifecycleExtKt {
method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.lifecycle.Lifecycle.State> currentStateAsState(androidx.lifecycle.Lifecycle);
}
diff --git a/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt b/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt
index 0686666..5b19f2b 100644
--- a/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt
+++ b/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt
@@ -8,6 +8,10 @@
method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, T initialValue, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
}
+ public final class LifecycleEffectKt {
+ method @androidx.compose.runtime.Composable public static void LifecycleEventEffect(androidx.lifecycle.Lifecycle.Event event, optional androidx.lifecycle.LifecycleOwner lifecycleOwner, kotlin.jvm.functions.Function0<kotlin.Unit> onEvent);
+ }
+
public final class LifecycleExtKt {
method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.lifecycle.Lifecycle.State> currentStateAsState(androidx.lifecycle.Lifecycle);
}
diff --git a/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/CollectAsStateWithLifecycleTests.kt b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/CollectAsStateWithLifecycleTests.kt
index 54b6553c..e9387cd 100644
--- a/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/CollectAsStateWithLifecycleTests.kt
+++ b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/CollectAsStateWithLifecycleTests.kt
@@ -19,6 +19,8 @@
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -27,7 +29,10 @@
import kotlinx.coroutines.runBlocking
import org.junit.Rule
import org.junit.Test
+import org.junit.runner.RunWith
+@MediumTest
+@RunWith(AndroidJUnit4::class)
class CollectAsStateWithLifecycleTests {
@get:Rule
diff --git a/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleEffectTest.kt b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleEffectTest.kt
new file mode 100644
index 0000000..3d7e9fe
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleEffectTest.kt
@@ -0,0 +1,113 @@
+/*
+ * 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.lifecycle.compose
+
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.setMain
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class LifecycleEffectTest {
+
+ private val dispatcher = UnconfinedTestDispatcher()
+ private lateinit var lifecycleOwner: TestLifecycleOwner
+
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Before
+ fun setup() {
+ lifecycleOwner = TestLifecycleOwner(coroutineDispatcher = dispatcher)
+ Dispatchers.setMain(dispatcher)
+ }
+
+ @Test
+ fun lifecycleEventEffectTest_noEvent() {
+ var stopCount = 0
+
+ composeTestRule.waitForIdle()
+ composeTestRule.setContent {
+ CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
+ LifecycleEventEffect(Lifecycle.Event.ON_STOP) {
+ stopCount++
+ }
+ }
+ }
+
+ composeTestRule.runOnIdle {
+ assertWithMessage("Lifecycle should not have been stopped")
+ .that(stopCount)
+ .isEqualTo(0)
+ }
+ }
+
+ @Test
+ fun lifecycleEventEffectTest_localLifecycleOwner() {
+ val expectedEvent = Lifecycle.Event.ON_STOP
+ var stopCount = 0
+
+ composeTestRule.waitForIdle()
+ composeTestRule.setContent {
+ CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
+ LifecycleEventEffect(expectedEvent) {
+ stopCount++
+ }
+ }
+ }
+
+ composeTestRule.runOnIdle {
+ lifecycleOwner.handleLifecycleEvent(expectedEvent)
+ assertWithMessage("Lifecycle should have been stopped")
+ .that(stopCount)
+ .isEqualTo(1)
+ }
+ }
+
+ @Test
+ fun lifecycleEventEffectTest_customLifecycleOwner() {
+ val expectedEvent = Lifecycle.Event.ON_STOP
+ var stopCount = 0
+
+ composeTestRule.waitForIdle()
+ composeTestRule.setContent {
+ LifecycleEventEffect(expectedEvent, lifecycleOwner) {
+ stopCount++
+ }
+ }
+
+ composeTestRule.runOnIdle {
+ lifecycleOwner.handleLifecycleEvent(expectedEvent)
+ assertWithMessage("Lifecycle should have been stopped")
+ .that(stopCount)
+ .isEqualTo(1)
+ }
+ }
+}
\ No newline at end of file
diff --git a/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleExtTest.kt b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleExtTest.kt
index 255d06a..a9e7d4e 100644
--- a/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleExtTest.kt
+++ b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleExtTest.kt
@@ -19,14 +19,16 @@
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-@RunWith(JUnit4::class)
+@MediumTest
+@RunWith(AndroidJUnit4::class)
class LifecycleExtTest {
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
@@ -52,9 +54,12 @@
assertThat(realStateValue).isEqualTo(Lifecycle.State.INITIALIZED)
}
+ // TODO(b/280362188): commenting this portion out until bug is fixed
+ /*
lifecycleOwner.currentState = Lifecycle.State.RESUMED
rule.runOnIdle {
assertThat(realStateValue).isEqualTo(Lifecycle.State.RESUMED)
}
+ */
}
}
\ No newline at end of file
diff --git a/lifecycle/lifecycle-runtime-compose/src/main/java/androidx/lifecycle/compose/LifecycleEffect.kt b/lifecycle/lifecycle-runtime-compose/src/main/java/androidx/lifecycle/compose/LifecycleEffect.kt
new file mode 100644
index 0000000..c9e47ae
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-compose/src/main/java/androidx/lifecycle/compose/LifecycleEffect.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.lifecycle.compose
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+
+/**
+ * Schedule an effect to run when the [Lifecycle] receives a specific [Lifecycle.Event].
+ *
+ * Using a [LifecycleEventObserver] to listen for when [LifecycleEventEffect] enters
+ * the composition, [onEvent] will be launched when receiving the specified [event].
+ *
+ * This function should **not** be used to listen for [Lifecycle.Event.ON_DESTROY] because
+ * Compose stops recomposing after receiving a [Lifecycle.Event.ON_STOP] and will never be
+ * aware of an ON_DESTROY to launch [onEvent].
+ *
+ * This function should also **not** be used to launch tasks in response to callback
+ * events by way of storing callback data as a [Lifecycle.State] in a [MutableState].
+ * Instead, see [currentStateAsState] to obtain a [State<Lifecycle.State>][State]
+ * that may be used to launch jobs in response to state changes.
+ *
+ * @param event The [Lifecycle.Event] to listen for
+ * @param lifecycleOwner The lifecycle owner to attach an observer
+ * @param onEvent The effect to be launched when we receive an [event] callback
+ *
+ * @throws IllegalArgumentException if attempting to listen for [Lifecycle.Event.ON_DESTROY]
+ */
+@Composable
+fun LifecycleEventEffect(
+ event: Lifecycle.Event,
+ lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
+ onEvent: () -> Unit
+) {
+ if (event == Lifecycle.Event.ON_DESTROY) {
+ throw IllegalArgumentException("LifecycleEventEffect cannot be used to " +
+ "listen for Lifecycle.Event.ON_DESTROY, since Compose disposes of the " +
+ "composition before ON_DESTROY observers are invoked.")
+ }
+
+ // Safely update the current `onEvent` lambda when a new one is provided
+ val currentOnEvent by rememberUpdatedState(onEvent)
+ DisposableEffect(lifecycleOwner) {
+ val observer = LifecycleEventObserver { _, e ->
+ if (e == event) {
+ currentOnEvent()
+ }
+ }
+
+ lifecycleOwner.lifecycle.addObserver(observer)
+
+ onDispose {
+ lifecycleOwner.lifecycle.removeObserver(observer)
+ }
+ }
+}
\ No newline at end of file
diff --git a/lint-checks/src/main/java/androidx/com/android/tools/idea/lang/aidl/AidlLanguage.java b/lint-checks/src/main/java/androidx/com/android/tools/idea/lang/aidl/AidlLanguage.java
index 7c2d47d..0a10e27 100644
--- a/lint-checks/src/main/java/androidx/com/android/tools/idea/lang/aidl/AidlLanguage.java
+++ b/lint-checks/src/main/java/androidx/com/android/tools/idea/lang/aidl/AidlLanguage.java
@@ -22,14 +22,20 @@
* Android IDL Language.
*/
public class AidlLanguage extends Language {
+ private static final Object INSTANCE_LOCK = new Object();
+
public static final Language INSTANCE = getOrCreate();
private static Language getOrCreate() {
- Language lang = Language.findLanguageByID(ID);
- if (lang != null) {
- return lang;
+ // The Language class is not thread-safe, so this is a best-effort to avoid a race condition
+ // during our own access across multiple lint worker threads.
+ synchronized (INSTANCE_LOCK) {
+ Language lang = Language.findLanguageByID(ID);
+ if (lang != null) {
+ return lang;
+ }
+ return new AidlLanguage();
}
- return new AidlLanguage();
}
@NonNls private static final String ID = "AIDL";
diff --git a/navigation/CHANGELOG.md b/navigation/CHANGELOG.md
new file mode 100644
index 0000000..5c13fd8
--- /dev/null
+++ b/navigation/CHANGELOG.md
@@ -0,0 +1,22 @@
+# Log for changes in the Navigation library
+#
+# `Added`: for new features
+# `Changed`: for changes in existing functionality
+# `Deprecated`: for soon to be removed functionality
+# `Removed`: for now removed feature
+# `Fixed`: for any bug fixes
+# `Security`: in case of vulnerabilities
+#
+# Possible headings:
+# API Changes
+# Bug Fixes
+# Dependency Updates
+# Behavior Change
+# External Contributions
+
+# Unreleased
+
+### Dependency Updates
+
+* Changed dependency of Activity library from version 1.6.1 to version 1.7.1.
+
diff --git a/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/MainFragment.kt b/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/MainFragment.kt
index 083d17d..e820bfe 100644
--- a/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/MainFragment.kt
+++ b/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/MainFragment.kt
@@ -18,6 +18,7 @@
import android.graphics.Color
import android.os.Bundle
+import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -27,6 +28,7 @@
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
+import androidx.transition.Slide
/**
* Fragment used to show how to navigate to another destination
@@ -38,6 +40,8 @@
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
+ enterTransition = Slide(Gravity.RIGHT)
+ exitTransition = Slide(Gravity.LEFT)
return inflater.inflate(R.layout.main_fragment, container, false)
}
diff --git a/navigation/integration-tests/testapp/src/main/res/navigation/nav_main.xml b/navigation/integration-tests/testapp/src/main/res/navigation/nav_main.xml
index cea98bd..4db4d1d 100644
--- a/navigation/integration-tests/testapp/src/main/res/navigation/nav_main.xml
+++ b/navigation/integration-tests/testapp/src/main/res/navigation/nav_main.xml
@@ -23,31 +23,19 @@
android:name=".MainFragment"
android:label="@string/home">
<argument android:name="myarg" android:defaultValue="Home" />
- <action android:id="@+id/next" app:destination="@+id/first_screen"
- app:enterAnim="@anim/nav_default_enter_anim"
- app:exitAnim="@anim/nav_default_exit_anim"
- app:popEnterAnim="@anim/nav_default_pop_enter_anim"
- app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
+ <action android:id="@+id/next" app:destination="@+id/first_screen"/>
</fragment>
<fragment android:id="@+id/first_screen"
android:name="androidx.navigation.testapp.MainFragment"
android:label="@string/first">
<argument android:name="myarg" android:defaultValue="one" />
- <action android:id="@+id/next" app:destination="@+id/next_fragment"
- app:enterAnim="@anim/nav_default_enter_anim"
- app:exitAnim="@anim/nav_default_exit_anim"
- app:popEnterAnim="@anim/nav_default_pop_enter_anim"
- app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
+ <action android:id="@+id/next" app:destination="@+id/next_fragment"/>
</fragment>
<fragment android:id="@+id/next_fragment"
android:name="androidx.navigation.testapp.MainFragment"
android:label="@string/second">
<argument android:name="myarg" android:defaultValue="two" />
- <action android:id="@+id/next" app:destination="@+id/first_screen"
- app:enterAnim="@anim/nav_default_enter_anim"
- app:exitAnim="@anim/nav_default_exit_anim"
- app:popEnterAnim="@anim/nav_default_pop_enter_anim"
- app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
+ <action android:id="@+id/next" app:destination="@+id/first_screen"/>
</fragment>
</navigation>
<dialog
diff --git a/navigation/integration-tests/testapp/src/main/res/navigation/two_pane_navigation.xml b/navigation/integration-tests/testapp/src/main/res/navigation/two_pane_navigation.xml
index f7eb4da..63261cbf 100644
--- a/navigation/integration-tests/testapp/src/main/res/navigation/two_pane_navigation.xml
+++ b/navigation/integration-tests/testapp/src/main/res/navigation/two_pane_navigation.xml
@@ -20,51 +20,31 @@
android:name="androidx.navigation.testapp.MainFragment"
android:label="@string/first">
<argument android:name="myarg" android:defaultValue="one" />
- <action android:id="@+id/next" app:destination="@+id/second_fragment"
- app:enterAnim="@anim/nav_default_enter_anim"
- app:exitAnim="@anim/nav_default_exit_anim"
- app:popEnterAnim="@anim/nav_default_pop_enter_anim"
- app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
+ <action android:id="@+id/next" app:destination="@+id/second_fragment"/>
</fragment>
<fragment android:id="@+id/second_fragment"
android:name="androidx.navigation.testapp.MainFragment"
android:label="@string/second">
<argument android:name="myarg" android:defaultValue="two" />
- <action android:id="@+id/next" app:destination="@+id/third_fragment"
- app:enterAnim="@anim/nav_default_enter_anim"
- app:exitAnim="@anim/nav_default_exit_anim"
- app:popEnterAnim="@anim/nav_default_pop_enter_anim"
- app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
+ <action android:id="@+id/next" app:destination="@+id/third_fragment"/>
</fragment>
<fragment android:id="@+id/third_fragment"
android:name="androidx.navigation.testapp.MainFragment"
android:label="@string/third">
<argument android:name="myarg" android:defaultValue="three" />
- <action android:id="@+id/next" app:destination="@+id/fourth_fragment"
- app:enterAnim="@anim/nav_default_enter_anim"
- app:exitAnim="@anim/nav_default_exit_anim"
- app:popEnterAnim="@anim/nav_default_pop_enter_anim"
- app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
+ <action android:id="@+id/next" app:destination="@+id/fourth_fragment"/>
</fragment>
<fragment android:id="@+id/fourth_fragment"
android:name="androidx.navigation.testapp.MainFragment"
android:label="@string/fourth">
<argument android:name="myarg" android:defaultValue="four" />
- <action android:id="@+id/next" app:destination="@+id/fifth_fragment"
- app:enterAnim="@anim/nav_default_enter_anim"
- app:exitAnim="@anim/nav_default_exit_anim"
- app:popEnterAnim="@anim/nav_default_pop_enter_anim"
- app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
+ <action android:id="@+id/next" app:destination="@+id/fifth_fragment"/>
</fragment>
<fragment android:id="@+id/fifth_fragment"
android:name="androidx.navigation.testapp.MainFragment"
android:label="@string/fifth">
<argument android:name="myarg" android:defaultValue="five" />
- <action android:id="@+id/next" app:destination="@+id/first_fragment"
- app:enterAnim="@anim/nav_default_enter_anim"
- app:exitAnim="@anim/nav_default_exit_anim"
- app:popEnterAnim="@anim/nav_default_pop_enter_anim"
- app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
+ <action android:id="@+id/next" app:destination="@+id/first_fragment"/>
</fragment>
<dialog
android:id="@+id/learn_more"
diff --git a/navigation/navigation-common/build.gradle b/navigation/navigation-common/build.gradle
index 72b827e..954e0da 100644
--- a/navigation/navigation-common/build.gradle
+++ b/navigation/navigation-common/build.gradle
@@ -45,7 +45,7 @@
api(libs.kotlinStdlib)
testImplementation(project(":navigation:navigation-testing"))
- testImplementation("androidx.arch.core:core-testing:2.1.0")
+ testImplementation("androidx.arch.core:core-testing:2.2.0")
testImplementation(libs.junit)
testImplementation(libs.mockitoCore4)
testImplementation(libs.truth)
diff --git a/navigation/navigation-dynamic-features-runtime/build.gradle b/navigation/navigation-dynamic-features-runtime/build.gradle
index e56823a..e722e52 100644
--- a/navigation/navigation-dynamic-features-runtime/build.gradle
+++ b/navigation/navigation-dynamic-features-runtime/build.gradle
@@ -35,7 +35,7 @@
api(libs.playFeatureDelivery)
testImplementation(project(":navigation:navigation-testing"))
- testImplementation("androidx.arch.core:core-testing:2.1.0")
+ testImplementation("androidx.arch.core:core-testing:2.2.0")
testImplementation(libs.testCore)
testImplementation(libs.testExtJunit)
testImplementation(libs.testRunner)
diff --git a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt
index 4dc1246..a8e6f2d 100644
--- a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt
@@ -31,7 +31,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
-/* ktlint-disable no-unused-imports */ // https://github.com/pinterest/ktlint/issues/937
import org.mockito.Mockito.`when` as mockWhen
/* ktlint-enable unused-imports */
diff --git a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt
index deda0ab..9e0ac66 100644
--- a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt
@@ -22,11 +22,9 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-/* ktlint-disable no-unused-imports */ // https://github.com/pinterest/ktlint/issues/937
-import org.mockito.Mockito.`when` as mockWhen
-/* ktlint-enable unused-imports */
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
+import org.mockito.Mockito.`when` as mockWhen
@RunWith(JUnit4::class)
public class DynamicInstallManagerTest {
diff --git a/navigation/navigation-runtime/build.gradle b/navigation/navigation-runtime/build.gradle
index f3c6511..c7ebc80 100644
--- a/navigation/navigation-runtime/build.gradle
+++ b/navigation/navigation-runtime/build.gradle
@@ -25,7 +25,7 @@
dependencies {
api(project(":navigation:navigation-common"))
- api("androidx.activity:activity-ktx:1.6.1")
+ api("androidx.activity:activity-ktx:1.7.1")
api("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
api("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
api("androidx.annotation:annotation-experimental:1.1.0")
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 dcc0f5c..8b35f7b 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
@@ -610,6 +610,23 @@
@UiThreadTest
@Test
+ fun testNavigateNullGraph() {
+ val navController = createNavController()
+ val deepLinkRequest = NavDeepLinkRequest.Builder.fromUri(
+ Uri.parse("android-app://androidx.navigation.test/destination")
+ ).build()
+
+ val expected = assertFailsWith<IllegalArgumentException> {
+ navController.navigate(deepLinkRequest)
+ }
+ assertThat(expected.message).isEqualTo(
+ "Cannot navigate to $deepLinkRequest. Navigation graph has not " +
+ "been set for NavController $navController."
+ )
+ }
+
+ @UiThreadTest
+ @Test
fun testInvalidNavigateViaDeepLink() {
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 8486793..be6eafb 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
@@ -1772,6 +1772,10 @@
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
) {
+ requireNotNull(_graph) {
+ "Cannot navigate to $request. Navigation graph has not been set for " +
+ "NavController $this."
+ }
val deepLinkMatch = _graph!!.matchDeepLink(request)
if (deepLinkMatch != null) {
val destination = deepLinkMatch.destination
diff --git a/paging/integration-tests/testapp/build.gradle b/paging/integration-tests/testapp/build.gradle
index 5ae97ce..db00ce6 100644
--- a/paging/integration-tests/testapp/build.gradle
+++ b/paging/integration-tests/testapp/build.gradle
@@ -24,7 +24,7 @@
}
dependencies {
- implementation("androidx.arch.core:core-runtime:2.1.0")
+ implementation("androidx.arch.core:core-runtime:2.2.0")
implementation(projectOrArtifact(":room:room-ktx"))
implementation(projectOrArtifact(":room:room-rxjava2"))
implementation(projectOrArtifact(":room:room-paging"))
diff --git a/paging/paging-common/api/current.txt b/paging/paging-common/api/current.txt
index 10ec13b..2070710 100644
--- a/paging/paging-common/api/current.txt
+++ b/paging/paging-common/api/current.txt
@@ -46,7 +46,7 @@
method @AnyThread public void onInvalidated();
}
- public final class InvalidatingPagingSourceFactory<Key, Value> implements kotlin.jvm.functions.Function0<androidx.paging.PagingSource<Key,Value>> {
+ public final class InvalidatingPagingSourceFactory<Key, Value> implements androidx.paging.PagingSourceFactory<Key,Value> {
ctor public InvalidatingPagingSourceFactory(kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
method public void invalidate();
method public androidx.paging.PagingSource<Key,Value> invoke();
@@ -411,6 +411,10 @@
public static final class PagingSource.LoadResult.Page.Companion {
}
+ public fun interface PagingSourceFactory<Key, Value> extends kotlin.jvm.functions.Function0<androidx.paging.PagingSource<Key,Value>> {
+ method public operator androidx.paging.PagingSource<Key,Value> invoke();
+ }
+
public final class PagingState<Key, Value> {
ctor public PagingState(java.util.List<androidx.paging.PagingSource.LoadResult.Page<Key,Value>> pages, Integer? anchorPosition, androidx.paging.PagingConfig config, @IntRange(from=0L) int leadingPlaceholderCount);
method public Value? closestItemToPosition(int anchorPosition);
diff --git a/paging/paging-common/api/public_plus_experimental_current.txt b/paging/paging-common/api/public_plus_experimental_current.txt
index 07e6471..a5d3d01 100644
--- a/paging/paging-common/api/public_plus_experimental_current.txt
+++ b/paging/paging-common/api/public_plus_experimental_current.txt
@@ -49,7 +49,7 @@
@kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalPagingApi {
}
- public final class InvalidatingPagingSourceFactory<Key, Value> implements kotlin.jvm.functions.Function0<androidx.paging.PagingSource<Key,Value>> {
+ public final class InvalidatingPagingSourceFactory<Key, Value> implements androidx.paging.PagingSourceFactory<Key,Value> {
ctor public InvalidatingPagingSourceFactory(kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
method public void invalidate();
method public androidx.paging.PagingSource<Key,Value> invoke();
@@ -415,6 +415,10 @@
public static final class PagingSource.LoadResult.Page.Companion {
}
+ public fun interface PagingSourceFactory<Key, Value> extends kotlin.jvm.functions.Function0<androidx.paging.PagingSource<Key,Value>> {
+ method public operator androidx.paging.PagingSource<Key,Value> invoke();
+ }
+
public final class PagingState<Key, Value> {
ctor public PagingState(java.util.List<androidx.paging.PagingSource.LoadResult.Page<Key,Value>> pages, Integer? anchorPosition, androidx.paging.PagingConfig config, @IntRange(from=0L) int leadingPlaceholderCount);
method public Value? closestItemToPosition(int anchorPosition);
diff --git a/paging/paging-common/api/restricted_current.txt b/paging/paging-common/api/restricted_current.txt
index 10ec13b..2070710 100644
--- a/paging/paging-common/api/restricted_current.txt
+++ b/paging/paging-common/api/restricted_current.txt
@@ -46,7 +46,7 @@
method @AnyThread public void onInvalidated();
}
- public final class InvalidatingPagingSourceFactory<Key, Value> implements kotlin.jvm.functions.Function0<androidx.paging.PagingSource<Key,Value>> {
+ public final class InvalidatingPagingSourceFactory<Key, Value> implements androidx.paging.PagingSourceFactory<Key,Value> {
ctor public InvalidatingPagingSourceFactory(kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
method public void invalidate();
method public androidx.paging.PagingSource<Key,Value> invoke();
@@ -411,6 +411,10 @@
public static final class PagingSource.LoadResult.Page.Companion {
}
+ public fun interface PagingSourceFactory<Key, Value> extends kotlin.jvm.functions.Function0<androidx.paging.PagingSource<Key,Value>> {
+ method public operator androidx.paging.PagingSource<Key,Value> invoke();
+ }
+
public final class PagingState<Key, Value> {
ctor public PagingState(java.util.List<androidx.paging.PagingSource.LoadResult.Page<Key,Value>> pages, Integer? anchorPosition, androidx.paging.PagingConfig config, @IntRange(from=0L) int leadingPlaceholderCount);
method public Value? closestItemToPosition(int anchorPosition);
diff --git a/paging/paging-common/build.gradle b/paging/paging-common/build.gradle
index e2ce56b..00ad658 100644
--- a/paging/paging-common/build.gradle
+++ b/paging/paging-common/build.gradle
@@ -33,7 +33,7 @@
}
api("androidx.annotation:annotation:1.3.0")
- api("androidx.arch.core:core-common:2.1.0")
+ api("androidx.arch.core:core-common:2.2.0")
api(libs.kotlinStdlib)
api(libs.kotlinCoroutinesCore)
diff --git a/paging/paging-common/src/main/kotlin/androidx/paging/InvalidatingPagingSourceFactory.kt b/paging/paging-common/src/main/kotlin/androidx/paging/InvalidatingPagingSourceFactory.kt
index 93ddb2d..3f74d54 100644
--- a/paging/paging-common/src/main/kotlin/androidx/paging/InvalidatingPagingSourceFactory.kt
+++ b/paging/paging-common/src/main/kotlin/androidx/paging/InvalidatingPagingSourceFactory.kt
@@ -32,7 +32,7 @@
*/
public class InvalidatingPagingSourceFactory<Key : Any, Value : Any>(
private val pagingSourceFactory: () -> PagingSource<Key, Value>
-) : () -> PagingSource<Key, Value> {
+) : PagingSourceFactory<Key, Value> {
@VisibleForTesting
internal val pagingSources = CopyOnWriteArrayList<PagingSource<Key, Value>>()
diff --git a/paging/paging-common/src/main/kotlin/androidx/paging/PagingData.kt b/paging/paging-common/src/main/kotlin/androidx/paging/PagingData.kt
index c14bb57..8ff3d51 100644
--- a/paging/paging-common/src/main/kotlin/androidx/paging/PagingData.kt
+++ b/paging/paging-common/src/main/kotlin/androidx/paging/PagingData.kt
@@ -115,6 +115,15 @@
),
uiReceiver = NOOP_UI_RECEIVER,
hintReceiver = NOOP_HINT_RECEIVER,
+ cachedPageEvent = {
+ PageEvent.Insert.Refresh(
+ pages = listOf(TransformablePage(0, data)),
+ placeholdersBefore = 0,
+ placeholdersAfter = 0,
+ sourceLoadStates = LoadStates.IDLE,
+ mediatorLoadStates = null
+ )
+ }
)
/**
@@ -143,6 +152,15 @@
),
uiReceiver = NOOP_UI_RECEIVER,
hintReceiver = NOOP_HINT_RECEIVER,
+ cachedPageEvent = {
+ PageEvent.Insert.Refresh(
+ pages = listOf(TransformablePage(0, data)),
+ placeholdersBefore = 0,
+ placeholdersAfter = 0,
+ sourceLoadStates = sourceLoadStates,
+ mediatorLoadStates = mediatorLoadStates
+ )
+ }
)
}
diff --git a/paging/paging-common/src/main/kotlin/androidx/paging/PagingSourceFactory.kt b/paging/paging-common/src/main/kotlin/androidx/paging/PagingSourceFactory.kt
new file mode 100644
index 0000000..4ed3c7f
--- /dev/null
+++ b/paging/paging-common/src/main/kotlin/androidx/paging/PagingSourceFactory.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.paging
+
+/**
+ * Interface for a factory that generates [PagingSource].
+ *
+ * The factory extending this interface can be used to instantiate a [Pager] as the
+ * pagingSourceFactory.
+ */
+public fun interface PagingSourceFactory<Key : Any, Value : Any> : () -> PagingSource<Key, Value> {
+ /**
+ * Returns a new PagingSource instance.
+ *
+ * This function can be invoked by calling pagingSourceFactory() or pagingSourceFactory::invoke.
+ */
+ public override operator fun invoke(): PagingSource<Key, Value>
+}
\ No newline at end of file
diff --git a/paging/paging-compose/build.gradle b/paging/paging-compose/build.gradle
index 918280d..d31e914 100644
--- a/paging/paging-compose/build.gradle
+++ b/paging/paging-compose/build.gradle
@@ -32,6 +32,7 @@
api("androidx.compose.runtime:runtime:1.2.1")
androidTestImplementation(projectOrArtifact(":compose:ui:ui-test-junit4"))
+ androidTestImplementation projectOrArtifact(":compose:ui:ui-tooling")
androidTestImplementation(project(":compose:test-utils"))
androidTestImplementation(projectOrArtifact(":internal-testutils-paging"))
androidTestImplementation(libs.testRunner)
diff --git a/paging/paging-compose/samples/build.gradle b/paging/paging-compose/samples/build.gradle
index 243f2c3..6fd15b4 100644
--- a/paging/paging-compose/samples/build.gradle
+++ b/paging/paging-compose/samples/build.gradle
@@ -29,6 +29,7 @@
compileOnly(projectOrArtifact(":annotation:annotation-sampled"))
implementation(projectOrArtifact(":compose:foundation:foundation"))
implementation(projectOrArtifact(":compose:material:material"))
+ implementation projectOrArtifact(":compose:ui:ui-tooling")
implementation(project(":paging:paging-compose"))
}
diff --git a/paging/paging-compose/samples/src/main/java/androidx/paging/compose/samples/PagingPreviewSample.kt b/paging/paging-compose/samples/src/main/java/androidx/paging/compose/samples/PagingPreviewSample.kt
new file mode 100644
index 0000000..c05f075
--- /dev/null
+++ b/paging/paging-compose/samples/src/main/java/androidx/paging/compose/samples/PagingPreviewSample.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.paging.compose.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.sp
+import androidx.paging.PagingData
+import androidx.paging.compose.collectAsLazyPagingItems
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+@Sampled
+@Preview
+@Composable
+fun PagingPreview() {
+ /**
+ * The composable that displays data from LazyPagingItems.
+ *
+ * This composable is inlined only for the purposes of this sample. In production code,
+ * this function should be its own top-level function.
+ */
+ @Composable
+ fun DisplayPaging(flow: Flow<PagingData<String>>) {
+ // Flow of real data i.e. flow from a ViewModel, or flow of fake data i.e. from a Preview.
+ val lazyPagingItems = flow.collectAsLazyPagingItems()
+ LazyColumn(modifier = Modifier
+ .fillMaxSize()
+ .background(Color.Red)) {
+ items(count = lazyPagingItems.itemCount) { index ->
+ val item = lazyPagingItems[index]
+ Text(text = "$item", fontSize = 35.sp, color = Color.Black)
+ }
+ }
+ }
+
+ /**
+ * The preview function should be responsible for creating the fake data and passing it to the
+ * function that displays it.
+ */
+ // create list of fake data for preview
+ val fakeData = List(10) { "preview item $it" }
+ // create pagingData from a list of fake data
+ val pagingData = PagingData.from(fakeData)
+ // pass pagingData containing fake data to a MutableStateFlow
+ val fakeDataFlow = MutableStateFlow(pagingData)
+ // pass flow to composable
+ DisplayPaging(flow = fakeDataFlow)
+}
\ No newline at end of file
diff --git a/paging/paging-compose/src/androidTest/java/androidx/paging/compose/LazyPagingItemsPreviewTest.kt b/paging/paging-compose/src/androidTest/java/androidx/paging/compose/LazyPagingItemsPreviewTest.kt
new file mode 100644
index 0000000..7cfba6e
--- /dev/null
+++ b/paging/paging-compose/src/androidTest/java/androidx/paging/compose/LazyPagingItemsPreviewTest.kt
@@ -0,0 +1,104 @@
+/*
+ * 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.paging.compose
+
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalInspectionMode
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.tooling.PreviewActivity
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.paging.PagingData
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import org.junit.Rule
+import org.junit.Test
+
+class LazyPagingItemsPreviewTest {
+
+ @get:Rule
+ val composeTestRule = createAndroidComposeRule<PreviewActivity>()
+
+ @Test
+ fun pagingPreviewTest() {
+ composeTestRule.setContent {
+ PagingPreview()
+ }
+ for (i in 0..9) {
+ composeTestRule.onNodeWithTag("$i")
+ .assertIsDisplayed()
+ }
+ }
+
+ @Test
+ fun emptyPreview() {
+ composeTestRule.setContent {
+ EmptyPreview()
+ }
+ composeTestRule.onNodeWithTag("0")
+ .assertDoesNotExist()
+ }
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@Preview
+@Composable
+fun PagingPreview() {
+ val data = List(50) { it }
+ val flow = MutableStateFlow(PagingData.from(data))
+ CompositionLocalProvider(
+ LocalInspectionMode provides true,
+ ) {
+ // Use StandardTestDispatcher so we don't start collecting on PagingData
+ val lazyPagingItems = flow.collectAsLazyPagingItems(StandardTestDispatcher())
+ LazyColumn(Modifier.height(500.dp)) {
+ items(count = lazyPagingItems.itemCount) { index ->
+ val item = lazyPagingItems[index]
+ Spacer(Modifier.height(50.dp).fillParentMaxWidth().testTag("$item"))
+ }
+ }
+ }
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@Preview
+@Composable
+fun EmptyPreview() {
+ val data = emptyList<Int>()
+ val flow = MutableStateFlow(PagingData.from(data))
+ CompositionLocalProvider(
+ LocalInspectionMode provides true,
+ ) {
+ // Use StandardTestDispatcher so we don't start collecting on PagingData
+ val lazyPagingItems = flow.collectAsLazyPagingItems(StandardTestDispatcher())
+ LazyColumn(Modifier.height(500.dp)) {
+ items(count = lazyPagingItems.itemCount) { index ->
+ val item = lazyPagingItems[index]
+ Spacer(Modifier.height(50.dp).fillParentMaxWidth().testTag("$item"))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/paging/paging-compose/src/main/java/androidx/paging/compose/LazyPagingItems.kt b/paging/paging-compose/src/main/java/androidx/paging/compose/LazyPagingItems.kt
index 81a6901..0c0082d 100644
--- a/paging/paging-compose/src/main/java/androidx/paging/compose/LazyPagingItems.kt
+++ b/paging/paging-compose/src/main/java/androidx/paging/compose/LazyPagingItems.kt
@@ -52,6 +52,11 @@
* This instance can be used for Lazy foundations such as [LazyListScope.items] to display data
* received from the [Flow] of [PagingData].
*
+ * Previewing [LazyPagingItems] is supported on a list of mock data. See sample for how to preview
+ * mock data.
+ *
+ * @sample androidx.paging.compose.samples.PagingPreview
+ *
* @param T the type of value used by [PagingData].
*/
public class LazyPagingItems<T : Any> internal constructor(
diff --git a/paging/paging-runtime/build.gradle b/paging/paging-runtime/build.gradle
index 013bbd8..47fdac8 100644
--- a/paging/paging-runtime/build.gradle
+++ b/paging/paging-runtime/build.gradle
@@ -50,7 +50,7 @@
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testRunner)
- androidTestImplementation("androidx.arch.core:core-testing:2.1.0")
+ androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
androidTestImplementation(libs.truth)
androidTestImplementation(libs.kotlinTest)
androidTestImplementation(libs.kotlinCoroutinesTest)
diff --git a/paging/paging-rxjava2-ktx/build.gradle b/paging/paging-rxjava2-ktx/build.gradle
index b298111..a355219 100644
--- a/paging/paging-rxjava2-ktx/build.gradle
+++ b/paging/paging-rxjava2-ktx/build.gradle
@@ -31,7 +31,7 @@
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testRunner)
- androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+ androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
androidTestImplementation(libs.espressoCore)
}
diff --git a/paging/paging-rxjava2/build.gradle b/paging/paging-rxjava2/build.gradle
index da06741d..c830a91 100644
--- a/paging/paging-rxjava2/build.gradle
+++ b/paging/paging-rxjava2/build.gradle
@@ -25,7 +25,7 @@
dependencies {
api(project(":paging:paging-common"))
- api("androidx.arch.core:core-runtime:2.1.0")
+ api("androidx.arch.core:core-runtime:2.2.0")
api(libs.rxjava2)
implementation(libs.kotlinStdlib)
implementation(libs.kotlinCoroutinesRx2)
@@ -41,7 +41,7 @@
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testRunner)
- androidTestImplementation("androidx.arch.core:core-testing:2.1.0")
+ androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
samples(project(":paging:paging-samples"))
}
diff --git a/paging/paging-rxjava3/build.gradle b/paging/paging-rxjava3/build.gradle
index f69969b..dc27213 100644
--- a/paging/paging-rxjava3/build.gradle
+++ b/paging/paging-rxjava3/build.gradle
@@ -25,7 +25,7 @@
dependencies {
api(project(":paging:paging-common"))
- api("androidx.arch.core:core-runtime:2.1.0")
+ api("androidx.arch.core:core-runtime:2.2.0")
api(libs.rxjava3)
implementation(libs.kotlinStdlib)
implementation(libs.kotlinCoroutinesRx3)
@@ -41,7 +41,7 @@
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testRunner)
- androidTestImplementation("androidx.arch.core:core-testing:2.1.0")
+ androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
}
androidx {
diff --git a/paging/paging-testing/api/current.txt b/paging/paging-testing/api/current.txt
index 066939a..f10ecc1 100644
--- a/paging/paging-testing/api/current.txt
+++ b/paging/paging-testing/api/current.txt
@@ -26,7 +26,7 @@
}
public final class StaticListPagingSourceFactoryKt {
- method public static <Value> kotlin.jvm.functions.Function0<androidx.paging.PagingSource<java.lang.Integer,Value>> asPagingSourceFactory(kotlinx.coroutines.flow.Flow<java.util.List<Value>>, kotlinx.coroutines.CoroutineScope coroutineScope);
+ method public static <Value> androidx.paging.PagingSourceFactory<java.lang.Integer,Value> asPagingSourceFactory(kotlinx.coroutines.flow.Flow<java.util.List<Value>>, kotlinx.coroutines.CoroutineScope coroutineScope);
}
public final class TestPager<Key, Value> {
diff --git a/paging/paging-testing/api/public_plus_experimental_current.txt b/paging/paging-testing/api/public_plus_experimental_current.txt
index 066939a..f10ecc1 100644
--- a/paging/paging-testing/api/public_plus_experimental_current.txt
+++ b/paging/paging-testing/api/public_plus_experimental_current.txt
@@ -26,7 +26,7 @@
}
public final class StaticListPagingSourceFactoryKt {
- method public static <Value> kotlin.jvm.functions.Function0<androidx.paging.PagingSource<java.lang.Integer,Value>> asPagingSourceFactory(kotlinx.coroutines.flow.Flow<java.util.List<Value>>, kotlinx.coroutines.CoroutineScope coroutineScope);
+ method public static <Value> androidx.paging.PagingSourceFactory<java.lang.Integer,Value> asPagingSourceFactory(kotlinx.coroutines.flow.Flow<java.util.List<Value>>, kotlinx.coroutines.CoroutineScope coroutineScope);
}
public final class TestPager<Key, Value> {
diff --git a/paging/paging-testing/api/restricted_current.txt b/paging/paging-testing/api/restricted_current.txt
index 066939a..f10ecc1 100644
--- a/paging/paging-testing/api/restricted_current.txt
+++ b/paging/paging-testing/api/restricted_current.txt
@@ -26,7 +26,7 @@
}
public final class StaticListPagingSourceFactoryKt {
- method public static <Value> kotlin.jvm.functions.Function0<androidx.paging.PagingSource<java.lang.Integer,Value>> asPagingSourceFactory(kotlinx.coroutines.flow.Flow<java.util.List<Value>>, kotlinx.coroutines.CoroutineScope coroutineScope);
+ method public static <Value> androidx.paging.PagingSourceFactory<java.lang.Integer,Value> asPagingSourceFactory(kotlinx.coroutines.flow.Flow<java.util.List<Value>>, kotlinx.coroutines.CoroutineScope coroutineScope);
}
public final class TestPager<Key, Value> {
diff --git a/paging/paging-testing/src/main/java/androidx/paging/testing/StaticListPagingSourceFactory.kt b/paging/paging-testing/src/main/java/androidx/paging/testing/StaticListPagingSourceFactory.kt
index e44216e..3cf9165 100644
--- a/paging/paging-testing/src/main/java/androidx/paging/testing/StaticListPagingSourceFactory.kt
+++ b/paging/paging-testing/src/main/java/androidx/paging/testing/StaticListPagingSourceFactory.kt
@@ -19,6 +19,7 @@
import androidx.paging.InvalidatingPagingSourceFactory
import androidx.paging.PagingSource
import androidx.paging.Pager
+import androidx.paging.PagingSourceFactory
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
@@ -41,7 +42,7 @@
*/
public fun <Value : Any> Flow<@JvmSuppressWildcards List<Value>>.asPagingSourceFactory(
coroutineScope: CoroutineScope
-): () -> PagingSource<Int, Value> {
+): PagingSourceFactory<Int, Value> {
var data: List<Value>? = null
diff --git a/paging/paging-testing/src/test/kotlin/androidx/paging/testing/PagerFlowSnapshotTest.kt b/paging/paging-testing/src/test/kotlin/androidx/paging/testing/PagerFlowSnapshotTest.kt
index 42aaa0b5..312d95b 100644
--- a/paging/paging-testing/src/test/kotlin/androidx/paging/testing/PagerFlowSnapshotTest.kt
+++ b/paging/paging-testing/src/test/kotlin/androidx/paging/testing/PagerFlowSnapshotTest.kt
@@ -20,6 +20,7 @@
import androidx.paging.PagingConfig
import androidx.paging.PagingSource
import androidx.paging.PagingSource.LoadParams
+import androidx.paging.PagingSourceFactory
import androidx.paging.PagingState
import androidx.paging.cachedIn
import androidx.paging.insertSeparators
@@ -2355,9 +2356,9 @@
}
private class WrappedPagingSourceFactory(
- private val factory: () -> PagingSource<Int, Int>,
+ private val factory: PagingSourceFactory<Int, Int>,
private val loadDelay: Long,
-) : () -> PagingSource<Int, Int> {
+) : PagingSourceFactory<Int, Int> {
override fun invoke(): PagingSource<Int, Int> = TestPagingSource(factory(), loadDelay)
}
diff --git a/paging/paging-testing/src/test/kotlin/androidx/paging/testing/StaticListPagingSourceFactoryTest.kt b/paging/paging-testing/src/test/kotlin/androidx/paging/testing/StaticListPagingSourceFactoryTest.kt
index 6a2f2c1..eb28936 100644
--- a/paging/paging-testing/src/test/kotlin/androidx/paging/testing/StaticListPagingSourceFactoryTest.kt
+++ b/paging/paging-testing/src/test/kotlin/androidx/paging/testing/StaticListPagingSourceFactoryTest.kt
@@ -17,8 +17,8 @@
package androidx.paging.testing
import androidx.paging.PagingConfig
-import androidx.paging.PagingSource
import androidx.paging.PagingSource.LoadResult.Page
+import androidx.paging.PagingSourceFactory
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
@@ -47,7 +47,7 @@
@Test
fun emptyFlow() {
- val factory: () -> PagingSource<Int, Int> =
+ val factory: PagingSourceFactory<Int, Int> =
flowOf<List<Int>>().asPagingSourceFactory(testScope)
val pagingSource = factory()
val pager = TestPager(pagingSource, CONFIG)
@@ -64,7 +64,7 @@
List(20) { it }
)
- val factory: () -> PagingSource<Int, Int> =
+ val factory: PagingSourceFactory<Int, Int> =
flow.asPagingSourceFactory(testScope)
val pagingSource = factory()
val pager = TestPager(pagingSource, CONFIG)
@@ -85,7 +85,7 @@
emit(List(15) { it + 30 }) // second gen
}
- val factory: () -> PagingSource<Int, Int> =
+ val factory: PagingSourceFactory<Int, Int> =
flow.asPagingSourceFactory(testScope)
advanceTimeBy(1000)
@@ -117,7 +117,7 @@
val mutableFlow = MutableSharedFlow<List<Int>>()
val collectionScope = this.backgroundScope
- val factory: () -> PagingSource<Int, Int> =
+ val factory: PagingSourceFactory<Int, Int> =
mutableFlow.asPagingSourceFactory(collectionScope)
mutableFlow.emit(List(10) { it })
@@ -146,10 +146,10 @@
fun multipleFactories_fromSameFlow() = testScope.runTest {
val mutableFlow = MutableSharedFlow<List<Int>>()
- val factory1: () -> PagingSource<Int, Int> =
+ val factory1: PagingSourceFactory<Int, Int> =
mutableFlow.asPagingSourceFactory(testScope.backgroundScope)
- val factory2: () -> PagingSource<Int, Int> =
+ val factory2: PagingSourceFactory<Int, Int> =
mutableFlow.asPagingSourceFactory(testScope.backgroundScope)
mutableFlow.emit(List(10) { it })
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index e17b5c1..fec6fdf 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -25,6 +25,6 @@
kotlin.code.style=official
# Disable docs
androidx.enableDocumentation=false
-androidx.playground.snapshotBuildId=9971607
+androidx.playground.snapshotBuildId=10041883
androidx.playground.metalavaBuildId=10009114
androidx.studio.type=playground
diff --git a/preference/preference/res/values-fr/strings.xml b/preference/preference/res/values-fr/strings.xml
index 2b8a26a..1348f8c 100644
--- a/preference/preference/res/values-fr/strings.xml
+++ b/preference/preference/res/values-fr/strings.xml
@@ -3,7 +3,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="v7_preference_on" msgid="89551595707643515">"ACTIVÉ"</string>
<string name="v7_preference_off" msgid="3140233346420563315">"DÉSACTIVÉ"</string>
- <string name="expand_button_title" msgid="2427401033573778270">"Paramètres avancés"</string>
+ <string name="expand_button_title" msgid="2427401033573778270">"Avancé"</string>
<string name="summary_collapsed_preference_list" msgid="9167775378838880170">"<xliff:g id="CURRENT_ITEMS">%1$s</xliff:g>, <xliff:g id="ADDED_ITEMS">%2$s</xliff:g>"</string>
<string name="copy" msgid="6083905920877235314">"Copier"</string>
<string name="preference_copied" msgid="6685851473431805375">"\"<xliff:g id="SUMMARY">%1$s</xliff:g>\" copié dans le presse-papier."</string>
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/FledgeCtsDebuggableTest.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/FledgeCtsDebuggableTest.java
index 5927a01..de51a6e 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/FledgeCtsDebuggableTest.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/FledgeCtsDebuggableTest.java
@@ -20,8 +20,10 @@
import static org.junit.Assert.assertThrows;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.net.Uri;
+import android.util.Log;
import androidx.annotation.RequiresApi;
import androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig;
@@ -71,7 +73,7 @@
private static final String TAG = "FledgeCtsDebuggableTest";
// Configuration constants
- private static final int SDK_MAX_REQUEST_PERMITS_PER_SECOND = 1000;
+ private static final int SDK_MAX_REQUEST_PERMITS_PER_SECOND = 1;
// sleep time to prevent hitting rate limit, with a small tolerance to prevent edge
// case of max calls per second falling exactly within one second
private static final long DEFAULT_API_RATE_LIMIT_SLEEP_MS =
@@ -204,7 +206,7 @@
new CustomAudienceClient(sContext);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
- Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+ doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
}
@Test
@@ -225,7 +227,7 @@
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
- Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+ doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
@@ -288,7 +290,7 @@
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
- Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+ doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
@@ -343,7 +345,7 @@
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
- Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+ doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
@@ -400,7 +402,7 @@
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
- Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+ doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
@@ -447,7 +449,7 @@
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
- Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+ doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
@@ -505,7 +507,7 @@
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
- Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+ doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
@@ -551,7 +553,7 @@
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
- Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+ doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
@@ -616,7 +618,7 @@
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
- Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+ doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
@@ -673,14 +675,14 @@
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
- Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+ doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Wait to ensure that CA2 gets expired
- Thread.sleep(caTimeToExpireSeconds * 2 * 1000);
+ doSleep(caTimeToExpireSeconds * 2 * 1000);
// Running ad selection and asserting that the outcome is returned in < 10 seconds
AdSelectionOutcome outcome =
@@ -727,7 +729,7 @@
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
- Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+ doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
@@ -774,7 +776,7 @@
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
- Thread.sleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
+ doSleep(DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
@@ -808,6 +810,23 @@
assertThat(selectAdsException.getCause()).isInstanceOf(TimeoutException.class);
}
+ @SuppressLint("BanThreadSleep")
+ private static void doSleep(long timeout) {
+ Log.i(TAG, String.format("Starting to sleep for %d ms", timeout));
+ long currentTime = System.currentTimeMillis();
+ long wakeupTime = currentTime + timeout;
+ while (wakeupTime - currentTime > 0) {
+ Log.i(TAG, String.format("Time left to sleep: %d ms", wakeupTime - currentTime));
+ try {
+ Thread.sleep(wakeupTime - currentTime);
+ } catch (InterruptedException ignored) {
+
+ }
+ currentTime = System.currentTimeMillis();
+ }
+ Log.i(TAG, "Done sleeping");
+ }
+
private static Uri getUri(String authority, String path) {
return Uri.parse("https://" + authority + path);
}
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/parser/InterfaceParserTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/parser/InterfaceParserTest.kt
index 98bf7a3..d43d7fc 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/parser/InterfaceParserTest.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/parser/InterfaceParserTest.kt
@@ -311,6 +311,29 @@
}
@Test
+ fun callbackInheritance_fails() {
+ val source = Source.kotlin(
+ "com/mysdk/MySdk.kt", """
+ package com.mysdk
+ import androidx.privacysandbox.tools.PrivacySandboxService
+ import androidx.privacysandbox.tools.PrivacySandboxCallback
+
+ interface FooInterface {}
+
+ @PrivacySandboxService
+ interface MySdk {}
+
+ @PrivacySandboxCallback
+ interface MyCallback : FooInterface {}
+ """
+ )
+ checkSourceFails(source).containsExactlyErrors(
+ "Error in com.mysdk.MyCallback: annotated interface inherits prohibited types (" +
+ "FooInterface)."
+ )
+ }
+
+ @Test
fun methodWithImplementation_fails() {
checkSourceFails(serviceMethod("suspend fun foo(): Int = 1")).containsExactlyErrors(
"Error in com.mysdk.MySdk.foo: method cannot have default implementation."
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/input/com/mysdk/MySdk.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/input/com/mysdk/MySdk.kt
index 5acab21..c096f86 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/input/com/mysdk/MySdk.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/input/com/mysdk/MySdk.kt
@@ -75,7 +75,8 @@
val query: String,
val extraValues: List<InnerValue>,
val maybeValue: InnerValue?,
- val myInterface: MyInterface
+ val myInterface: MyInterface,
+ val myUiInterface: MyUiInterface,
)
@PrivacySandboxValue
@@ -85,7 +86,8 @@
data class Response(
val response: String,
val mySecondInterface: MySecondInterface,
- val maybeOtherInterface: MySecondInterface
+ val maybeOtherInterface: MySecondInterface,
+ val myUiInterface: MyUiInterface,
)
@PrivacySandboxCallback
@@ -95,4 +97,6 @@
fun onClick(x: Int, y: Int)
fun onCompleteInterface(myInterface: MyInterface)
+
+ fun onCompleteUiInterface(myUiInterface: MyUiInterface)
}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyCallbackClientProxy.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyCallbackClientProxy.kt
index bd4584b..b471257 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyCallbackClientProxy.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyCallbackClientProxy.kt
@@ -1,6 +1,7 @@
package com.mysdk
import android.content.Context
+import androidx.privacysandbox.ui.provider.toCoreLibInfo
public class MyCallbackClientProxy(
public val remote: IMyCallback,
@@ -17,4 +18,9 @@
public override fun onCompleteInterface(myInterface: MyInterface): Unit {
remote.onCompleteInterface(MyInterfaceStubDelegate(myInterface, context))
}
+
+ public override fun onCompleteUiInterface(myUiInterface: MyUiInterface): Unit {
+ remote.onCompleteUiInterface(IMyUiInterfaceCoreLibInfoAndBinderWrapperConverter.toParcelable(myUiInterface.toCoreLibInfo(context),
+ MyUiInterfaceStubDelegate(myUiInterface, context)))
+ }
}
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySdkStubDelegate.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySdkStubDelegate.kt
index 6dbf1b5f..0d5c413 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySdkStubDelegate.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySdkStubDelegate.kt
@@ -166,9 +166,10 @@
transactionCallback.onCancellable(cancellationSignal)
}
- public override fun acceptUiInterfaceParam(input: IMyUiInterface): Unit {
+ public override fun acceptUiInterfaceParam(input: IMyUiInterfaceCoreLibInfoAndBinderWrapper):
+ Unit {
coroutineScope.launch {
- delegate.acceptUiInterfaceParam((input as MyUiInterfaceStubDelegate).delegate)
+ delegate.acceptUiInterfaceParam((input.binder as MyUiInterfaceStubDelegate).delegate)
}
}
}
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/RequestConverter.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/RequestConverter.kt
index 10b33c0..dba30f0 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/RequestConverter.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/RequestConverter.kt
@@ -1,6 +1,7 @@
package com.mysdk
import android.content.Context
+import androidx.privacysandbox.ui.provider.toCoreLibInfo
public class RequestConverter(
public val context: Context,
@@ -12,7 +13,9 @@
InnerValueConverter(context).fromParcelable(it) }.toList(),
maybeValue = parcelable.maybeValue?.let { notNullValue ->
InnerValueConverter(context).fromParcelable(notNullValue) },
- myInterface = (parcelable.myInterface as MyInterfaceStubDelegate).delegate)
+ myInterface = (parcelable.myInterface as MyInterfaceStubDelegate).delegate,
+ myUiInterface = (parcelable.myUiInterface.binder as
+ MyUiInterfaceStubDelegate).delegate)
return annotatedValue
}
@@ -24,6 +27,9 @@
parcelable.maybeValue = annotatedValue.maybeValue?.let { notNullValue ->
InnerValueConverter(context).toParcelable(notNullValue) }
parcelable.myInterface = MyInterfaceStubDelegate(annotatedValue.myInterface, context)
+ parcelable.myUiInterface =
+ IMyUiInterfaceCoreLibInfoAndBinderWrapperConverter.toParcelable(annotatedValue.myUiInterface.toCoreLibInfo(context),
+ MyUiInterfaceStubDelegate(annotatedValue.myUiInterface, context))
return parcelable
}
}
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/ResponseConverter.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/ResponseConverter.kt
index 8995c2b..2337bdf 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/ResponseConverter.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/ResponseConverter.kt
@@ -1,6 +1,7 @@
package com.mysdk
import android.content.Context
+import androidx.privacysandbox.ui.provider.toCoreLibInfo
public class ResponseConverter(
public val context: Context,
@@ -11,7 +12,9 @@
mySecondInterface = (parcelable.mySecondInterface as
MySecondInterfaceStubDelegate).delegate,
maybeOtherInterface = (parcelable.maybeOtherInterface as
- MySecondInterfaceStubDelegate).delegate)
+ MySecondInterfaceStubDelegate).delegate,
+ myUiInterface = (parcelable.myUiInterface.binder as
+ MyUiInterfaceStubDelegate).delegate)
return annotatedValue
}
@@ -22,6 +25,9 @@
MySecondInterfaceStubDelegate(annotatedValue.mySecondInterface, context)
parcelable.maybeOtherInterface =
MySecondInterfaceStubDelegate(annotatedValue.maybeOtherInterface, context)
+ parcelable.myUiInterface =
+ IMyUiInterfaceCoreLibInfoAndBinderWrapperConverter.toParcelable(annotatedValue.myUiInterface.toCoreLibInfo(context),
+ MyUiInterfaceStubDelegate(annotatedValue.myUiInterface, context))
return parcelable
}
}
diff --git a/privacysandbox/tools/tools-apigenerator/build.gradle b/privacysandbox/tools/tools-apigenerator/build.gradle
index 5ddde21..61b6753 100644
--- a/privacysandbox/tools/tools-apigenerator/build.gradle
+++ b/privacysandbox/tools/tools-apigenerator/build.gradle
@@ -15,7 +15,6 @@
*/
import androidx.build.LibraryType
-import androidx.build.RunApiTasks
import androidx.build.SdkHelperKt
import androidx.build.SupportConfig
@@ -78,5 +77,4 @@
type = LibraryType.OTHER_CODE_PROCESSOR
inceptionYear = "2022"
description = "Generate sources for communication with SDKs in the Privacy Sandbox."
- runApiTasks = new RunApiTasks.No("API still under active development.")
}
diff --git a/privacysandbox/tools/tools-apigenerator/src/main/java/androidx/privacysandbox/tools/apigenerator/PrivacySandboxApiGenerator.kt b/privacysandbox/tools/tools-apigenerator/src/main/java/androidx/privacysandbox/tools/apigenerator/PrivacySandboxApiGenerator.kt
index 6ae6736..e43ec79 100644
--- a/privacysandbox/tools/tools-apigenerator/src/main/java/androidx/privacysandbox/tools/apigenerator/PrivacySandboxApiGenerator.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/main/java/androidx/privacysandbox/tools/apigenerator/PrivacySandboxApiGenerator.kt
@@ -23,6 +23,7 @@
import androidx.privacysandbox.tools.core.generator.BinderCodeConverter
import androidx.privacysandbox.tools.core.generator.ClientBinderCodeConverter
import androidx.privacysandbox.tools.core.generator.ClientProxyTypeGenerator
+import androidx.privacysandbox.tools.core.generator.CoreLibInfoAndBinderWrapperConverterGenerator
import androidx.privacysandbox.tools.core.generator.GenerationTarget
import androidx.privacysandbox.tools.core.generator.PrivacySandboxExceptionFileGenerator
import androidx.privacysandbox.tools.core.generator.PrivacySandboxCancellationExceptionFileGenerator
@@ -111,6 +112,7 @@
)
generateValueConverters(api, binderCodeConverter, output)
generateSuspendFunctionUtilities(api, basePackageName, output)
+ generateCoreLibInfoConverters(api, output)
}
private fun generateBinders(api: ParsedApi, aidlCompiler: AidlCompiler, output: File) {
@@ -180,6 +182,12 @@
}
}
+ private fun generateCoreLibInfoConverters(api: ParsedApi, output: File) {
+ api.interfaces.filter { it.inheritsSandboxedUiAdapter }.map {
+ CoreLibInfoAndBinderWrapperConverterGenerator.generate(it).writeTo(output)
+ }
+ }
+
private fun unzipDescriptorsFileAndParseStubs(
sdkInterfaceDescriptors: Path,
outputDirectory: Path,
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/CallbacksApiGeneratorDiffTest.kt b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/CallbacksApiGeneratorDiffTest.kt
index 2c60fa7..81ec136 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/CallbacksApiGeneratorDiffTest.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/CallbacksApiGeneratorDiffTest.kt
@@ -27,5 +27,7 @@
"com/sdkwithcallbacks/ISdkCallback.java",
"com/sdkwithcallbacks/ISdkService.java",
"com/sdkwithcallbacks/ParcelableResponse.java",
+ "com/sdkwithcallbacks/IMyUiInterface.java",
+ "com/sdkwithcallbacks/IMyUiInterfaceCoreLibInfoAndBinderWrapper.java"
)
}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/ValuesApiGeneratorDiffTest.kt b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/ValuesApiGeneratorDiffTest.kt
index 526e75b..03f4de0 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/ValuesApiGeneratorDiffTest.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/ValuesApiGeneratorDiffTest.kt
@@ -24,6 +24,7 @@
override val subdirectoryName = "values"
override val relativePathsToExpectedAidlClasses = listOf(
"com/sdkwithvalues/IMyInterface.java",
+ "com/sdkwithvalues/IMyUiInterface.java",
"com/sdkwithvalues/ISdkInterface.java",
"com/sdkwithvalues/ISdkResponseTransactionCallback.java",
"com/sdkwithvalues/IListSdkResponseTransactionCallback.java",
@@ -33,5 +34,6 @@
"com/sdkwithvalues/ICancellationSignal.java",
"com/sdkwithvalues/ParcelableStackFrame.java",
"com/sdkwithvalues/PrivacySandboxThrowableParcel.java",
+ "com/sdkwithvalues/IMyUiInterfaceCoreLibInfoAndBinderWrapper.java"
)
}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt
index 3c40caa0..4c4c071 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt
@@ -225,7 +225,7 @@
class NonAnnotatedClass
"""
), Source.java(
- "com/mysdk/NonAnnotatedJavaClass.java", """
+ "com/mysdk/NonAnnotatedJavaClass", """
package com.mysdk;
class NonAnnotatedJavaClass {}
"""
@@ -265,7 +265,7 @@
@Test
fun nonKotlinAnnotatedInterface_throws() {
val source = Source.java(
- "com/mysdk/MySdk.java", """
+ "com/mysdk/MySdk", """
package com.mysdk;
import androidx.privacysandbox.tools.PrivacySandboxService;
@PrivacySandboxService
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/input/com/sdkwithcallbacks/SdkService.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/input/com/sdkwithcallbacks/SdkService.kt
index c4be47f..8f50038 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/input/com/sdkwithcallbacks/SdkService.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/input/com/sdkwithcallbacks/SdkService.kt
@@ -4,6 +4,7 @@
import androidx.privacysandbox.tools.PrivacySandboxCallback
import androidx.privacysandbox.tools.PrivacySandboxValue
import androidx.privacysandbox.tools.PrivacySandboxInterface
+import androidx.privacysandbox.ui.core.SandboxedUiAdapter
@PrivacySandboxService
interface SdkService {
@@ -22,9 +23,14 @@
}
@PrivacySandboxValue
-data class Response(val response: String)
+data class Response(val response: String, val uiInterface: MyUiInterface)
@PrivacySandboxInterface
interface MyInterface {
fun doStuff()
-}
\ No newline at end of file
+}
+
+@PrivacySandboxInterface
+interface MyUiInterface : SandboxedUiAdapter {
+ fun doUiStuff()
+}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/IMyUiInterfaceCoreLibInfoAndBinderWrapperConverter.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/IMyUiInterfaceCoreLibInfoAndBinderWrapperConverter.kt
new file mode 100644
index 0000000..b0baa3615
--- /dev/null
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/IMyUiInterfaceCoreLibInfoAndBinderWrapperConverter.kt
@@ -0,0 +1,13 @@
+package com.sdkwithcallbacks
+
+import android.os.Bundle
+
+public object IMyUiInterfaceCoreLibInfoAndBinderWrapperConverter {
+ public fun toParcelable(coreLibInfo: Bundle, `interface`: IMyUiInterface):
+ IMyUiInterfaceCoreLibInfoAndBinderWrapper {
+ val parcelable = IMyUiInterfaceCoreLibInfoAndBinderWrapper()
+ parcelable.coreLibInfo = coreLibInfo
+ parcelable.binder = `interface`
+ return parcelable
+ }
+}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterface.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterface.kt
new file mode 100644
index 0000000..2975910
--- /dev/null
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterface.kt
@@ -0,0 +1,7 @@
+package com.sdkwithcallbacks
+
+import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+
+public interface MyUiInterface : SandboxedUiAdapter {
+ public fun doUiStuff(): Unit
+}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterfaceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterfaceClientProxy.kt
new file mode 100644
index 0000000..00ccc68
--- /dev/null
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterfaceClientProxy.kt
@@ -0,0 +1,32 @@
+package com.sdkwithcallbacks
+
+import android.content.Context
+import android.os.Bundle
+import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
+import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+import androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient
+import java.util.concurrent.Executor
+
+public class MyUiInterfaceClientProxy(
+ public val remote: IMyUiInterface,
+ public val coreLibInfo: Bundle,
+) : MyUiInterface {
+ public val sandboxedUiAdapter: SandboxedUiAdapter =
+ SandboxedUiAdapterFactory.createFromCoreLibInfo(coreLibInfo)
+
+ public override fun doUiStuff(): Unit {
+ remote.doUiStuff()
+ }
+
+ public override fun openSession(
+ context: Context,
+ initialWidth: Int,
+ initialHeight: Int,
+ isZOrderOnTop: Boolean,
+ clientExecutor: Executor,
+ client: SandboxedUiAdapter.SessionClient,
+ ): Unit {
+ sandboxedUiAdapter.openSession(context, initialWidth, initialHeight, isZOrderOnTop,
+ clientExecutor, client)
+ }
+}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/Response.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/Response.kt
index 569c215..a449083 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/Response.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/Response.kt
@@ -2,4 +2,5 @@
public data class Response(
public val response: String,
+ public val uiInterface: MyUiInterface,
)
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/ResponseConverter.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/ResponseConverter.kt
index e1d05a5..e5105f8 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/ResponseConverter.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/ResponseConverter.kt
@@ -3,13 +3,18 @@
public object ResponseConverter {
public fun fromParcelable(parcelable: ParcelableResponse): Response {
val annotatedValue = Response(
- response = parcelable.response)
+ response = parcelable.response,
+ uiInterface = MyUiInterfaceClientProxy(parcelable.uiInterface.binder,
+ parcelable.uiInterface.coreLibInfo))
return annotatedValue
}
public fun toParcelable(annotatedValue: Response): ParcelableResponse {
val parcelable = ParcelableResponse()
parcelable.response = annotatedValue.response
+ parcelable.uiInterface =
+ IMyUiInterfaceCoreLibInfoAndBinderWrapperConverter.toParcelable((annotatedValue.uiInterface
+ as MyUiInterfaceClientProxy).coreLibInfo, annotatedValue.uiInterface.remote)
return parcelable
}
}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/IMySecondInterfaceCoreLibInfoAndBinderWrapperConverter.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/IMySecondInterfaceCoreLibInfoAndBinderWrapperConverter.kt
new file mode 100644
index 0000000..cc90541
--- /dev/null
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/IMySecondInterfaceCoreLibInfoAndBinderWrapperConverter.kt
@@ -0,0 +1,13 @@
+package com.sdk
+
+import android.os.Bundle
+
+public object IMySecondInterfaceCoreLibInfoAndBinderWrapperConverter {
+ public fun toParcelable(coreLibInfo: Bundle, `interface`: IMySecondInterface):
+ IMySecondInterfaceCoreLibInfoAndBinderWrapper {
+ val parcelable = IMySecondInterfaceCoreLibInfoAndBinderWrapper()
+ parcelable.coreLibInfo = coreLibInfo
+ parcelable.binder = `interface`
+ return parcelable
+ }
+}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterfaceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterfaceClientProxy.kt
index e5d6745..05069c3 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterfaceClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterfaceClientProxy.kt
@@ -31,12 +31,14 @@
public override fun doSomething(firstInterface: MyInterface,
secondInterface: MySecondInterface): Unit {
- remote.doSomething((firstInterface as MyInterfaceClientProxy).remote, (secondInterface as
- MySecondInterfaceClientProxy).remote)
+ remote.doSomething((firstInterface as MyInterfaceClientProxy).remote,
+ IMySecondInterfaceCoreLibInfoAndBinderWrapperConverter.toParcelable((secondInterface
+ as MySecondInterfaceClientProxy).coreLibInfo, secondInterface.remote))
}
public override fun doSomethingWithNullableInterface(maybeInterface: MySecondInterface?): Unit {
- remote.doSomethingWithNullableInterface(maybeInterface?.let { notNullValue -> (notNullValue
- as MySecondInterfaceClientProxy).remote })
+ remote.doSomethingWithNullableInterface(maybeInterface?.let { notNullValue ->
+ IMySecondInterfaceCoreLibInfoAndBinderWrapperConverter.toParcelable((notNullValue as
+ MySecondInterfaceClientProxy).coreLibInfo, notNullValue.remote) })
}
}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySdkClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySdkClientProxy.kt
index 19a398a..578aa60 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySdkClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySdkClientProxy.kt
@@ -1,6 +1,5 @@
package com.sdk
-import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
import com.sdk.PrivacySandboxThrowableParcelConverter
import com.sdk.PrivacySandboxThrowableParcelConverter.fromThrowableParcel
import kotlin.coroutines.resumeWithException
@@ -42,7 +41,7 @@
}
override fun onSuccess(result: IMySecondInterfaceCoreLibInfoAndBinderWrapper) {
it.resumeWith(Result.success(MySecondInterfaceClientProxy(result.binder,
- SandboxedUiAdapterFactory.createFromCoreLibInfo(result.coreLibInfo))))
+ result.coreLibInfo)))
}
override fun onFailure(throwableParcel: PrivacySandboxThrowableParcel) {
it.resumeWithException(fromThrowableParcel(throwableParcel))
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterfaceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterfaceClientProxy.kt
index 9880002..2b6a244 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterfaceClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterfaceClientProxy.kt
@@ -1,14 +1,19 @@
package com.sdk
import android.content.Context
+import android.os.Bundle
+import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
import androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient
import java.util.concurrent.Executor
public class MySecondInterfaceClientProxy(
public val remote: IMySecondInterface,
- public val sandboxedUiAdapter: SandboxedUiAdapter,
+ public val coreLibInfo: Bundle,
) : MySecondInterface {
+ public val sandboxedUiAdapter: SandboxedUiAdapter =
+ SandboxedUiAdapterFactory.createFromCoreLibInfo(coreLibInfo)
+
public override fun doStuff(): Unit {
remote.doStuff()
}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/input/com/sdkwithvalues/SdkInterface.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/input/com/sdkwithvalues/SdkInterface.kt
index d3441d4..91b8904 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/input/com/sdkwithvalues/SdkInterface.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/input/com/sdkwithvalues/SdkInterface.kt
@@ -3,6 +3,7 @@
import androidx.privacysandbox.tools.PrivacySandboxInterface
import androidx.privacysandbox.tools.PrivacySandboxService
import androidx.privacysandbox.tools.PrivacySandboxValue
+import androidx.privacysandbox.ui.core.SandboxedUiAdapter
@PrivacySandboxService
interface SdkInterface {
@@ -23,6 +24,7 @@
val floatingPoint: Float,
val hugeNumber: Double,
val myInterface: MyInterface,
+ val myUiInterface: MyUiInterface,
val numbers: List<Int>,
val maybeNumber: Int?,
val maybeInterface: MyInterface?,
@@ -42,4 +44,9 @@
@PrivacySandboxInterface
interface MyInterface {
fun doStuff()
+}
+
+@PrivacySandboxInterface
+interface MyUiInterface : SandboxedUiAdapter {
+ fun doUiStuff()
}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/IMyUiInterfaceCoreLibInfoAndBinderWrapperConverter.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/IMyUiInterfaceCoreLibInfoAndBinderWrapperConverter.kt
new file mode 100644
index 0000000..58dbae1
--- /dev/null
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/IMyUiInterfaceCoreLibInfoAndBinderWrapperConverter.kt
@@ -0,0 +1,13 @@
+package com.sdkwithvalues
+
+import android.os.Bundle
+
+public object IMyUiInterfaceCoreLibInfoAndBinderWrapperConverter {
+ public fun toParcelable(coreLibInfo: Bundle, `interface`: IMyUiInterface):
+ IMyUiInterfaceCoreLibInfoAndBinderWrapper {
+ val parcelable = IMyUiInterfaceCoreLibInfoAndBinderWrapper()
+ parcelable.coreLibInfo = coreLibInfo
+ parcelable.binder = `interface`
+ return parcelable
+ }
+}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/InnerSdkValue.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/InnerSdkValue.kt
index bf0b49f..392ac9e 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/InnerSdkValue.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/InnerSdkValue.kt
@@ -9,6 +9,7 @@
public val floatingPoint: Float,
public val hugeNumber: Double,
public val myInterface: MyInterface,
+ public val myUiInterface: MyUiInterface,
public val numbers: List<Int>,
public val maybeNumber: Int?,
public val maybeInterface: MyInterface?,
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/InnerSdkValueConverter.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/InnerSdkValueConverter.kt
index 2ce2e99..aa7cbf4 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/InnerSdkValueConverter.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/InnerSdkValueConverter.kt
@@ -11,6 +11,8 @@
floatingPoint = parcelable.floatingPoint,
hugeNumber = parcelable.hugeNumber,
myInterface = MyInterfaceClientProxy(parcelable.myInterface),
+ myUiInterface = MyUiInterfaceClientProxy(parcelable.myUiInterface.binder,
+ parcelable.myUiInterface.coreLibInfo),
numbers = parcelable.numbers.toList(),
maybeNumber = parcelable.maybeNumber.firstOrNull(),
maybeInterface = parcelable.maybeInterface?.let { notNullValue ->
@@ -28,6 +30,9 @@
parcelable.floatingPoint = annotatedValue.floatingPoint
parcelable.hugeNumber = annotatedValue.hugeNumber
parcelable.myInterface = (annotatedValue.myInterface as MyInterfaceClientProxy).remote
+ parcelable.myUiInterface =
+ IMyUiInterfaceCoreLibInfoAndBinderWrapperConverter.toParcelable((annotatedValue.myUiInterface
+ as MyUiInterfaceClientProxy).coreLibInfo, annotatedValue.myUiInterface.remote)
parcelable.numbers = annotatedValue.numbers.toIntArray()
parcelable.maybeNumber = if (annotatedValue.maybeNumber == null) intArrayOf() else
intArrayOf(annotatedValue.maybeNumber)
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterface.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterface.kt
new file mode 100644
index 0000000..65cbc2b
--- /dev/null
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterface.kt
@@ -0,0 +1,7 @@
+package com.sdkwithvalues
+
+import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+
+public interface MyUiInterface : SandboxedUiAdapter {
+ public fun doUiStuff(): Unit
+}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterfaceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterfaceClientProxy.kt
new file mode 100644
index 0000000..7a2c6e0
--- /dev/null
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterfaceClientProxy.kt
@@ -0,0 +1,32 @@
+package com.sdkwithvalues
+
+import android.content.Context
+import android.os.Bundle
+import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
+import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+import androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient
+import java.util.concurrent.Executor
+
+public class MyUiInterfaceClientProxy(
+ public val remote: IMyUiInterface,
+ public val coreLibInfo: Bundle,
+) : MyUiInterface {
+ public val sandboxedUiAdapter: SandboxedUiAdapter =
+ SandboxedUiAdapterFactory.createFromCoreLibInfo(coreLibInfo)
+
+ public override fun doUiStuff(): Unit {
+ remote.doUiStuff()
+ }
+
+ public override fun openSession(
+ context: Context,
+ initialWidth: Int,
+ initialHeight: Int,
+ isZOrderOnTop: Boolean,
+ clientExecutor: Executor,
+ client: SandboxedUiAdapter.SessionClient,
+ ): Unit {
+ sandboxedUiAdapter.openSession(context, initialWidth, initialHeight, isZOrderOnTop,
+ clientExecutor, client)
+ }
+}
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt
index ec307d5..2cf4f2f 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt
@@ -169,18 +169,7 @@
addParameter("cancellationSignal", cancellationSignalType())
}
addMethod("onSuccess") {
- val interfaceType = api.interfaceMap[type]
- if (interfaceType != null && interfaceType.inheritsSandboxedUiAdapter) {
- // Bypass getAidlTypeDeclaration, since we want to specify the UI wrapper
- // parcelable rather than the interface.
- addParameter(
- "result",
- AidlTypeSpec(
- interfaceType.uiAdapterAidlWrapper(),
- kind = AidlTypeKind.PARCELABLE
- )
- )
- } else if (type != Types.unit) {
+ if (type != Types.unit) {
addParameter(Parameter("result", type))
}
}
@@ -269,7 +258,12 @@
val type = wrapWithListIfNeeded(rawType)
api.valueMap[type]?.let { return it.aidlType() }
api.callbackMap[type]?.let { return it.aidlType() }
- api.interfaceMap[type]?.let { return it.aidlType() }
+ api.interfaceMap[type]?.let {
+ if (it.inheritsSandboxedUiAdapter) {
+ return AidlTypeSpec(it.uiAdapterAidlWrapper(), kind = AidlTypeKind.PARCELABLE)
+ }
+ return it.aidlType()
+ }
return when (type.qualifiedName) {
Boolean::class.qualifiedName -> primitive("boolean")
Int::class.qualifiedName -> primitive("int")
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientBinderCodeConverter.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientBinderCodeConverter.kt
index a97ab95..103a40a 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientBinderCodeConverter.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientBinderCodeConverter.kt
@@ -16,6 +16,8 @@
package androidx.privacysandbox.tools.core.generator
+import androidx.privacysandbox.tools.core.generator.SpecNames.contextPropertyName
+import androidx.privacysandbox.tools.core.generator.ValueConverterFileGenerator.Companion.toParcelableMethodName
import androidx.privacysandbox.tools.core.model.AnnotatedInterface
import androidx.privacysandbox.tools.core.model.AnnotatedValue
import androidx.privacysandbox.tools.core.model.ParsedApi
@@ -25,10 +27,6 @@
import com.squareup.kotlinpoet.TypeName
class ClientBinderCodeConverter(api: ParsedApi) : BinderCodeConverter(api) {
- companion object {
- val sandboxedUiAdapterFactoryClass =
- ClassName("androidx.privacysandbox.ui.client", "SandboxedUiAdapterFactory")
- }
override fun convertToInterfaceModelCode(
annotatedInterface: AnnotatedInterface,
@@ -36,10 +34,9 @@
): CodeBlock {
if (annotatedInterface.inheritsSandboxedUiAdapter) {
return CodeBlock.of(
- "%T(%L.binder, %T.createFromCoreLibInfo(%L.coreLibInfo))",
+ "%T(%L.binder, %L.coreLibInfo)",
annotatedInterface.clientProxyNameSpec(),
expression,
- sandboxedUiAdapterFactoryClass,
expression,
)
}
@@ -49,10 +46,30 @@
override fun convertToInterfaceBinderCode(
annotatedInterface: AnnotatedInterface,
expression: String
- ): CodeBlock =
- CodeBlock.of(
+ ): CodeBlock {
+ if (annotatedInterface.inheritsSandboxedUiAdapter) {
+ return CodeBlock.builder().build {
+ addNamed(
+ "%coreLibInfoConverter:T.%toParcelable:N(" +
+ "(%interface:L as %clientProxy:T).coreLibInfo, " +
+ "%interface:L.remote)",
+ hashMapOf<String, Any>(
+ "coreLibInfoConverter" to ClassName(
+ annotatedInterface.type.packageName,
+ annotatedInterface.coreLibInfoConverterName()
+ ),
+ "toParcelable" to toParcelableMethodName,
+ "interface" to expression,
+ "context" to contextPropertyName,
+ "clientProxy" to annotatedInterface.clientProxyNameSpec()
+ )
+ )
+ }
+ }
+ return CodeBlock.of(
"(%L as %T).remote", expression, annotatedInterface.clientProxyNameSpec()
)
+ }
override fun convertToInterfaceBinderType(annotatedInterface: AnnotatedInterface): TypeName {
if (annotatedInterface.inheritsSandboxedUiAdapter) {
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientProxyTypeGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientProxyTypeGenerator.kt
index 12e389c..e82bafb 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientProxyTypeGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientProxyTypeGenerator.kt
@@ -41,6 +41,8 @@
) {
private val cancellationSignalClassName = ClassName(basePackageName, "ICancellationSignal")
private val sandboxedUiAdapterPropertyName = "sandboxedUiAdapter"
+ private val sandboxedUiAdapterFactoryClass =
+ ClassName("androidx.privacysandbox.ui.client", "SandboxedUiAdapterFactory")
/**
* Generates a ClientProxy for this interface.
@@ -70,15 +72,24 @@
)
}
if (inheritsUiAdapter) add(
- PropertySpec.builder(
- sandboxedUiAdapterPropertyName, Types.sandboxedUiAdapter.poetTypeName()
- ).addModifiers(KModifier.PUBLIC).build()
+ PropertySpec.builder("coreLibInfo", SpecNames.bundleClass)
+ .addModifiers(KModifier.PUBLIC).build()
)
})
addFunctions(annotatedInterface.methods.map(::toFunSpec))
if (inheritsUiAdapter) {
+ addProperty(
+ PropertySpec.builder(
+ sandboxedUiAdapterPropertyName, Types.sandboxedUiAdapter.poetTypeName()
+ ).addModifiers(KModifier.PUBLIC)
+ .initializer(
+ "%T.createFromCoreLibInfo(coreLibInfo)",
+ sandboxedUiAdapterFactoryClass
+ )
+ .build()
+ )
addFunction(generateOpenSession())
}
}
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ServerBinderCodeConverter.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ServerBinderCodeConverter.kt
index 302c4a9..623aa51 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ServerBinderCodeConverter.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ServerBinderCodeConverter.kt
@@ -25,14 +25,22 @@
import androidx.privacysandbox.tools.core.model.ParsedApi
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
+import com.squareup.kotlinpoet.TypeName
class ServerBinderCodeConverter(private val api: ParsedApi) : BinderCodeConverter(api) {
override fun convertToInterfaceModelCode(
annotatedInterface: AnnotatedInterface,
expression: String
- ): CodeBlock = CodeBlock.of(
- "(%L as %T).delegate", expression, annotatedInterface.stubDelegateNameSpec()
- )
+ ): CodeBlock {
+ if (annotatedInterface.inheritsSandboxedUiAdapter) {
+ return CodeBlock.of(
+ "(%L.binder as %T).delegate", expression, annotatedInterface.stubDelegateNameSpec()
+ )
+ }
+ return CodeBlock.of(
+ "(%L as %T).delegate", expression, annotatedInterface.stubDelegateNameSpec()
+ )
+ }
override fun convertToInterfaceBinderCode(
annotatedInterface: AnnotatedInterface,
@@ -67,8 +75,12 @@
)
}
- override fun convertToInterfaceBinderType(annotatedInterface: AnnotatedInterface) =
- annotatedInterface.aidlType().innerType.poetTypeName()
+ override fun convertToInterfaceBinderType(annotatedInterface: AnnotatedInterface): TypeName {
+ if (annotatedInterface.inheritsSandboxedUiAdapter) {
+ return annotatedInterface.uiAdapterAidlWrapper().poetTypeName()
+ }
+ return annotatedInterface.aidlType().innerType.poetTypeName()
+ }
override fun convertToValueBinderCode(value: AnnotatedValue, expression: String): CodeBlock =
CodeBlock.of(
diff --git a/privacysandbox/tools/tools/api/current.txt b/privacysandbox/tools/tools/api/current.txt
index 2083825..7b7f6e8 100644
--- a/privacysandbox/tools/tools/api/current.txt
+++ b/privacysandbox/tools/tools/api/current.txt
@@ -1,7 +1,16 @@
// Signature format: 4.0
package androidx.privacysandbox.tools {
- @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface PrivacySandboxService {
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface PrivacySandboxCallback {
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface PrivacySandboxInterface {
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface PrivacySandboxService {
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface PrivacySandboxValue {
}
}
diff --git a/privacysandbox/tools/tools/api/public_plus_experimental_current.txt b/privacysandbox/tools/tools/api/public_plus_experimental_current.txt
index 2083825..7b7f6e8 100644
--- a/privacysandbox/tools/tools/api/public_plus_experimental_current.txt
+++ b/privacysandbox/tools/tools/api/public_plus_experimental_current.txt
@@ -1,7 +1,16 @@
// Signature format: 4.0
package androidx.privacysandbox.tools {
- @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface PrivacySandboxService {
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface PrivacySandboxCallback {
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface PrivacySandboxInterface {
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface PrivacySandboxService {
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface PrivacySandboxValue {
}
}
diff --git a/privacysandbox/tools/tools/api/restricted_current.txt b/privacysandbox/tools/tools/api/restricted_current.txt
index 2083825..7b7f6e8 100644
--- a/privacysandbox/tools/tools/api/restricted_current.txt
+++ b/privacysandbox/tools/tools/api/restricted_current.txt
@@ -1,7 +1,16 @@
// Signature format: 4.0
package androidx.privacysandbox.tools {
- @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface PrivacySandboxService {
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface PrivacySandboxCallback {
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface PrivacySandboxInterface {
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface PrivacySandboxService {
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface PrivacySandboxValue {
}
}
diff --git a/privacysandbox/tools/tools/build.gradle b/privacysandbox/tools/tools/build.gradle
index ab1a8d2..587326b 100644
--- a/privacysandbox/tools/tools/build.gradle
+++ b/privacysandbox/tools/tools/build.gradle
@@ -15,7 +15,6 @@
*/
import androidx.build.LibraryType
-import androidx.build.RunApiTasks
plugins {
id("AndroidXPlugin")
@@ -32,5 +31,4 @@
type = LibraryType.PUBLISHED_LIBRARY
inceptionYear = "2022"
description = "Tools for communicating with SDKs running in the Privacy Sandbox."
- runApiTasks = new RunApiTasks.No("API still under active development.")
}
diff --git a/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxCallback.kt b/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxCallback.kt
index 9ef876b..ea4c5e2 100644
--- a/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxCallback.kt
+++ b/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxCallback.kt
@@ -17,11 +17,25 @@
package androidx.privacysandbox.tools
/**
- * Annotated callbacks that can be passed to an SDK running in the privacy sandbox.
+ * Annotated callbacks that can be passed to an SDK running in the Privacy Sandbox.
*
- * Callbacks should be public interfaces that only declare functions without implementation.
- * Callback functions should be fire-and-forget: non-suspending functions that have no return value.
+ * These can be used to provide the SDK with a channel to invoke app code, e.g. listeners. They
+ * should be public interfaces that only declare functions without implementation, and they may not
+ * extend any other interface. Callbacks run in the main thread by default.
+ *
+ * The allowed types are the same as for [PrivacySandboxInterface], except that functions may not
+ * return a value.
+ *
+ * Usage example:
+ * ```
+ * @PrivacySandboxCallback
+ * interface MyCallback {
+ * fun onComplete(response: Response)
+ * fun onClick(x: Int, y: Int)
+ * fun onCompleteInterface(myInterface: MyInterface)
+ * }
+ * ```
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
-annotation class PrivacySandboxCallback
\ No newline at end of file
+public annotation class PrivacySandboxCallback
\ No newline at end of file
diff --git a/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxInterface.kt b/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxInterface.kt
index 281a4d0..1971378 100644
--- a/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxInterface.kt
+++ b/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxInterface.kt
@@ -16,9 +16,45 @@
package androidx.privacysandbox.tools
+import java.util.concurrent.CancellationException
+
/**
- * Annotated interfaces used by the client to communicate with the SDK in the privacy sandbox.
+ * Annotated interfaces used by the app to communicate with the SDK in the Privacy Sandbox.
+ *
+ * Functions in a [PrivacySandboxInterface] annotated interface must obey the following rules:
+ * - Functions with return values must suspend
+ * - Parameter types may be primitives, [PrivacySandboxValue], [PrivacySandboxCallback],
+ * [PrivacySandboxInterface], or lists of primitives or [PrivacySandboxValue]. Nullable types
+ * are allowed.
+ * - Return types may be primitives, [PrivacySandboxValue], [PrivacySandboxInterface], or lists
+ * of primitives or [PrivacySandboxValue]. Nullable types are allowed.
+ *
+ * Suspend functions operate as follows:
+ * - The main thread is used by default
+ * - App cancellations are propagated to SDK coroutines
+ * - Cancellation exceptions thrown by SDKs are packaged and rethrown as valid
+ * [CancellationException]s
+ *
+ * Additionally, all exceptions thrown by SDK suspend function implementations are wrapped and
+ * rethrown to app developers as `PrivacySandboxException` with a full stack trace. Errors in
+ * non-suspend functions will not be rethrown.
+ *
+ * [PrivacySandboxInterface] annotated interfaces may not extend any interface except for
+ * [androidx.privacysandbox.ui.core.SandboxedUiAdapter], which can be used to provide SDK content in
+ * an app's UI. These interfaces may also have any other functions that are normally allowed.
+ *
+ * Usage example:
+ * ```
+ * @PrivacySandboxInterface
+ * interface MyInterface {
+ * suspend fun doSomething(request: Request): Response
+ * suspend fun getMyInterface(input: MyInterface): MyInterface
+ * suspend fun getNullableInterface(input: MySecondInterface): MySecondInterface?
+ * fun setListener(listener: MyCallback)
+ * fun appendValue(x: Int)
+ * }
+ * ```
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
-annotation class PrivacySandboxInterface
\ No newline at end of file
+public annotation class PrivacySandboxInterface
\ No newline at end of file
diff --git a/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxService.kt b/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxService.kt
index c1ddb6c..02a9792 100644
--- a/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxService.kt
+++ b/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxService.kt
@@ -16,7 +16,42 @@
package androidx.privacysandbox.tools
-/** Entry point for an SDK service running in the privacy sandbox. */
+/** Entry point for an SDK service running in the Privacy Sandbox.
+ *
+ * There must be exactly one interface annotated with @PrivacySandboxService in your SDK module.
+ * This will be the first point of communication once the app has successfully loaded your SDK
+ * in the Privacy Sandbox.
+ *
+ * On the SDK side, the tools will generate a class called `AbstractSandboxedSdkProviderCompat`,
+ * containing an abstract factory method to create this service. This must be implemented by SDK
+ * developers, returning an implementation of the [PrivacySandboxService] annotated interface. This
+ * implementation should then be named in the SDK's `build.gradle` as the
+ * `compatSdkProviderClassName`.
+ *
+ * For example:
+ * ```
+ * package com.example.mysdk
+ *
+ * class MySdkSandboxedSdkProvider : AbstractSandboxedSdkProviderCompat() {
+ * override fun createMySdk(context: Context): MySdk = MySdkImpl(context)
+ * }
+ * ```
+ *
+ * On the app side, the tools will generate a factory class for the service containing a static
+ * function (prefixed with "wrapTo") which should be used to convert an IBinder to the
+ * [PrivacySandboxService] annotated interface.
+ *
+ * For example:
+ * ```
+ * val sandboxManagerCompat = SdkSandboxManagerCompat.from(this)
+ * val sandboxedSdk = sandboxManagerCompat.loadSdk("com.example.mysdk", Bundle.EMPTY)
+ * val mySdk = MySdkFactory.wrapToMySdk(sandboxedSdk.getInterface()!!)
+ * ```
+ *
+ * Like [PrivacySandboxInterface], interfaces annotated with [PrivacySandboxService] have
+ * restrictions on allowed methods and types. See [PrivacySandboxInterface] documentation for
+ * details.
+ */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
-annotation class PrivacySandboxService
\ No newline at end of file
+public annotation class PrivacySandboxService
\ No newline at end of file
diff --git a/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxValue.kt b/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxValue.kt
index b9ceb51..7b002d9 100644
--- a/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxValue.kt
+++ b/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxValue.kt
@@ -20,9 +20,26 @@
* Annotated values that can be sent to/from SDKs in the Privacy Sandbox.
*
* The values should be public Kotlin data classes that only contain immutable properties with types
- * supported by the sandbox (primitives, another [PrivacySandboxValue], etc.)
+ * supported by the sandbox (primitives, [PrivacySandboxInterface], [PrivacySandboxValue], or lists
+ * of primitives or [PrivacySandboxValue]). [PrivacySandboxCallback] interfaces are not allowed.
+ *
* Values cannot have functions, type parameters or properties with default values.
+ *
+ * Usage example:
+ * ```
+ * @PrivacySandboxValue
+ * data class ComplicatedStructure(
+ * val id: Int,
+ * val separator: Char,
+ * val message: String,
+ * val hugeNumber: Double,
+ * val myInterface: MyInterface,
+ * val numbers: List<Int>,
+ * val maybeNumber: Int?,
+ * val maybeInterface: MyInterface?,
+ * )
+ * ```
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
-annotation class PrivacySandboxValue
+public annotation class PrivacySandboxValue
diff --git a/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/internal/GeneratedPublicApi.kt b/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/internal/GeneratedPublicApi.kt
index f20c52d..71e15e1 100644
--- a/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/internal/GeneratedPublicApi.kt
+++ b/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/internal/GeneratedPublicApi.kt
@@ -31,4 +31,4 @@
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-annotation class GeneratedPublicApi
\ No newline at end of file
+public annotation class GeneratedPublicApi
\ No newline at end of file
diff --git a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/BenchmarkOperation.java b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/BenchmarkOperation.java
index c57f7b4..f9b60ea 100644
--- a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/BenchmarkOperation.java
+++ b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/BenchmarkOperation.java
@@ -32,10 +32,15 @@
@NonNull ProfileInstallReceiver.ResultDiagnostics callback
) {
File shaderDirectory;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- // shaders started using code cache dir once it was added in N
- shaderDirectory = Api24ContextHelper.getDeviceProtectedCodeCacheDir(context);
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (Build.VERSION.SDK_INT >= 34) {
+ // U switched to cache dir, so it's not deleted on each app update
+ shaderDirectory = Api24ContextHelper.createDeviceProtectedStorageContext(context)
+ .getCacheDir();
+ } else if (Build.VERSION.SDK_INT >= 24) {
+ // shaders started using device protected storage context once it was added in N
+ shaderDirectory = Api21ContextHelper.getCodeCacheDir(
+ Api24ContextHelper.createDeviceProtectedStorageContext(context));
+ } else if (Build.VERSION.SDK_INT == 23) {
// getCodeCacheDir was added in L, but not used by platform for shaders until M
shaderDirectory = Api21ContextHelper.getCodeCacheDir(context);
} else {
@@ -82,9 +87,9 @@
@RequiresApi(api = Build.VERSION_CODES.N)
private static class Api24ContextHelper {
- static File getDeviceProtectedCodeCacheDir(Context context) {
+ static Context createDeviceProtectedStorageContext(Context context) {
// Code device protected storage added in 24
- return context.createDeviceProtectedStorageContext().getCodeCacheDir();
+ return context.createDeviceProtectedStorageContext();
}
}
}
diff --git a/room/benchmark/build.gradle b/room/benchmark/build.gradle
index 7758551..d10928f 100644
--- a/room/benchmark/build.gradle
+++ b/room/benchmark/build.gradle
@@ -30,7 +30,7 @@
// depend on the shadowed version so that it tests with the shipped artifact
kaptAndroidTest project(path: ":room:room-compiler", configuration: "shadowAndImplementation")
androidTestImplementation(project(":room:room-rxjava2"))
- androidTestImplementation("androidx.arch.core:core-runtime:2.0.1")
+ androidTestImplementation("androidx.arch.core:core-runtime:2.2.0")
androidTestImplementation(projectOrArtifact(":benchmark:benchmark-junit4"))
androidTestImplementation(libs.rxjava2)
androidTestImplementation(libs.junit)
diff --git a/room/integration-tests/autovaluetestapp/build.gradle b/room/integration-tests/autovaluetestapp/build.gradle
index c2b23f1..e44eda7 100644
--- a/room/integration-tests/autovaluetestapp/build.gradle
+++ b/room/integration-tests/autovaluetestapp/build.gradle
@@ -29,7 +29,7 @@
dependencies {
implementation(project(":room:room-common"))
implementation(project(":room:room-runtime"))
- implementation(projectOrArtifact(":arch:core:core-runtime"))
+ implementation("androidx.arch.core:core-runtime:2.2.0")
// depend on the shadowed version so that it tests with the shipped artifact
androidTestAnnotationProcessor project(path: ":room:room-compiler",
@@ -37,9 +37,8 @@
androidTestAnnotationProcessor(libs.autoValue)
androidTestAnnotationProcessor(libs.autoValueParcel)
- androidTestImplementation(projectOrArtifact(":arch:core:core-runtime")) // Added for b/155802460
androidTestImplementation(project(":room:room-testing"))
- androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+ androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
androidTestImplementation(libs.autoValueAnnotations)
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testCore)
diff --git a/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt b/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt
index 8720b6e..7d9085e 100644
--- a/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt
+++ b/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt
@@ -18,6 +18,8 @@
import androidx.testutils.gradle.ProjectSetupRule
import com.google.common.truth.Expect
+import java.io.File
+import java.nio.file.Files
import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.TaskOutcome
@@ -26,11 +28,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
-import java.io.File
-import java.nio.file.Files
-import javax.xml.parsers.DocumentBuilderFactory
-import javax.xml.xpath.XPathConstants
-import javax.xml.xpath.XPathFactory
@RunWith(Parameterized::class)
class RoomIncrementalAnnotationProcessingTest(
@@ -110,27 +107,7 @@
* prebuilts (SNAPSHOT).
*/
private val roomVersion by lazy {
- val metadataFile = File(projectSetup.props.tipOfTreeMavenRepoPath).resolve(
- "androidx/room/room-compiler/maven-metadata.xml"
- )
- check(metadataFile.exists()) {
- "Cannot find room metadata file in ${metadataFile.absolutePath}"
- }
- check(metadataFile.isFile) {
- "Metadata file should be a file but it is not."
- }
- val xmlDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
- .parse(metadataFile)
- val latestVersionNode = XPathFactory.newInstance().newXPath()
- .compile("/metadata/versioning/latest").evaluate(
- xmlDoc, XPathConstants.STRING
- )
- check(latestVersionNode is String) {
- """Unexpected node for latest version:
- $latestVersionNode / ${latestVersionNode::class.java}
- """.trimIndent()
- }
- latestVersionNode
+ projectSetup.getLibraryLatestVersionInLocalRepo("androidx/room/room-compiler")
}
@Before
diff --git a/room/integration-tests/kotlintestapp/build.gradle b/room/integration-tests/kotlintestapp/build.gradle
index 3bd5882..8241224 100644
--- a/room/integration-tests/kotlintestapp/build.gradle
+++ b/room/integration-tests/kotlintestapp/build.gradle
@@ -75,9 +75,8 @@
implementation(project(":room:room-common"))
implementation(project(":room:room-runtime"))
implementation(project(":room:room-paging"))
- implementation(projectOrArtifact(":arch:core:core-runtime"))
- implementation(projectOrArtifact(":lifecycle:lifecycle-livedata"))
- implementation(projectOrArtifact(":lifecycle:lifecycle-livedata-ktx"))
+ implementation("androidx.arch.core:core-runtime:2.2.0")
+ implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
implementation(libs.kotlinStdlib)
implementation(libs.kotlinCoroutinesAndroid)
implementation(libs.multidex)
@@ -92,8 +91,8 @@
kaptAndroidTestWithKapt(
project(path: ":room:room-compiler", configuration: "shadowAndImplementation")
)
- androidTestImplementation(projectOrArtifact(":arch:core:core-runtime"))
- androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-livedata-ktx"))
+ androidTestImplementation("androidx.arch.core:core-runtime:2.2.0")
+ androidTestImplementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testRunner) {
@@ -119,9 +118,9 @@
androidTestImplementation(project(":room:room-rxjava3"))
androidTestImplementation(project(":room:room-ktx"))
androidTestImplementation(project(":internal-testutils-common"))
- androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+ androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
androidTestImplementation("androidx.paging:paging-runtime:3.1.1")
- androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime-testing"))
+ androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.6.1")
androidTestImplementation(libs.rxjava2)
androidTestImplementation(libs.kotlinCoroutinesTest)
testImplementation(libs.mockitoCore4)
diff --git a/room/integration-tests/testapp/build.gradle b/room/integration-tests/testapp/build.gradle
index d81c2ef..1975a13 100644
--- a/room/integration-tests/testapp/build.gradle
+++ b/room/integration-tests/testapp/build.gradle
@@ -93,9 +93,9 @@
dependencies {
implementation(project(":room:room-common"))
implementation(project(":room:room-runtime"))
- implementation(projectOrArtifact(":arch:core:core-runtime"))
- implementation(projectOrArtifact(":lifecycle:lifecycle-livedata"))
- implementation(projectOrArtifact(":lifecycle:lifecycle-runtime"))
+ implementation("androidx.arch.core:core-runtime:2.2.0")
+ implementation("androidx.lifecycle:lifecycle-livedata:2.6.1")
+ implementation("androidx.lifecycle:lifecycle-runtime:2.6.1")
implementation(libs.multidex)
// Workaround for b/191286558.
@@ -113,18 +113,18 @@
configuration: "shadowAndImplementation")
androidTestImplementation(project(":annotation:annotation-experimental"))
- androidTestImplementation(projectOrArtifact(":arch:core:core-runtime"))
- androidTestImplementation(projectOrArtifact(":arch:core:core-common"))
+ androidTestImplementation("androidx.arch.core:core-runtime:2.2.0")
+ androidTestImplementation("androidx.arch.core:core-common:2.2.0")
androidTestImplementation(project(":room:room-testing"))
androidTestImplementation(project(":room:room-rxjava2"))
androidTestImplementation(project(":room:room-rxjava3"))
androidTestImplementation(project(":room:room-guava"))
androidTestImplementation(project(":room:room-paging"))
- androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+ androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
androidTestImplementation(projectOrArtifact(":paging:paging-runtime"))
- androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime"))
- androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime-testing"))
- androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-livedata"))
+ androidTestImplementation("androidx.lifecycle:lifecycle-runtime:2.6.1")
+ androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.6.1")
+ androidTestImplementation("androidx.lifecycle:lifecycle-livedata:2.6.1")
// guavaAndroid is a implementation dependency instead of androidTestImplementation because
// if it's in the androidTest apk it causes the test APK to be multidex in ways that break
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/Source.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/Source.kt
index c62cfab..4860763 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/Source.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/Source.kt
@@ -79,6 +79,9 @@
@Language("java")
code: String
): Source {
+ require(!qName.endsWith(".java")) {
+ "Please exclude extension `.java` for Java sources."
+ }
return JavaSource(
qName,
code
@@ -90,6 +93,9 @@
@Language("kotlin")
code: String
): Source {
+ require(filePath.endsWith(".kt")) {
+ "Please include extension `.kt` for Kotlin sources."
+ }
return KotlinSource(
filePath,
code
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/TestKotlinCompiler.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/TestKotlinCompiler.kt
index 81e443e..8287cfd 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/TestKotlinCompiler.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/TestKotlinCompiler.kt
@@ -101,7 +101,7 @@
if (hasKotlinSource) return this
return copy(
sources = sources + Source.kotlin(
- "SyntheticSource",
+ "SyntheticSource.kt",
code = """
package xprocessing.generated
class SyntheticKotlinSource
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt
index 7ded92a..cf0ef44 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt
@@ -185,7 +185,7 @@
@Test
fun cleanKotlinCompilationHasNoWarnings() {
val kotlinSource = Source.kotlin(
- "Subject",
+ "Subject.kt",
"""
package foo.bar
class Subject {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt
index 470c9af..213eeb9 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt
@@ -22,10 +22,10 @@
import com.google.devtools.ksp.symbol.KSDeclaration
import com.google.devtools.ksp.symbol.KSNode
import com.google.devtools.ksp.symbol.KSType
+import com.google.devtools.ksp.symbol.KSTypeAlias
import com.google.devtools.ksp.symbol.KSTypeArgument
import com.google.devtools.ksp.symbol.KSTypeParameter
import com.google.devtools.ksp.symbol.KSTypeReference
-import com.google.devtools.ksp.symbol.Modifier
/**
* Root package comes as <root> instead of "" so we work around it here.
@@ -47,10 +47,12 @@
}
internal fun KSTypeReference.isTypeParameterReference(): Boolean {
- return this.resolve().declaration is KSTypeParameter
+ return this.resolve().isTypeParameter()
}
-fun KSType.isInline() = declaration.modifiers.contains(Modifier.INLINE)
+internal fun KSType.isTypeParameter(): Boolean {
+ return declaration is KSTypeParameter
+}
internal fun KSType.withNullability(nullability: XNullability) = when (nullability) {
XNullability.NULLABLE -> makeNullable()
@@ -59,14 +61,15 @@
}
private fun KSAnnotated.hasAnnotation(qName: String) =
- annotations.any { it.hasQualifiedName(qName) }
+ annotations.any { it.hasQualifiedNameOrAlias(qName) }
-private fun KSAnnotation.hasQualifiedName(qName: String): Boolean {
- return annotationType.resolve().hasQualifiedName(qName)
+private fun KSAnnotation.hasQualifiedNameOrAlias(qName: String): Boolean {
+ return annotationType.resolve().hasQualifiedNameOrAlias(qName)
}
-private fun KSType.hasQualifiedName(qName: String): Boolean {
- return declaration.qualifiedName?.asString() == qName
+private fun KSType.hasQualifiedNameOrAlias(qName: String): Boolean {
+ return declaration.qualifiedName?.asString() == qName ||
+ (declaration as? KSTypeAlias)?.type?.resolve()?.hasQualifiedNameOrAlias(qName) ?: false
}
internal fun KSAnnotated.hasJvmWildcardAnnotation() =
@@ -75,20 +78,55 @@
internal fun KSAnnotated.hasSuppressJvmWildcardAnnotation() =
hasAnnotation(JvmSuppressWildcards::class.java.canonicalName!!)
-private fun KSType.hasAnnotation(qName: String) = annotations.any { it.hasQualifiedName(qName) }
-
-internal fun KSType.hasJvmWildcardAnnotation() =
- hasAnnotation(JvmWildcard::class.java.canonicalName!!)
+// TODO(bcorso): There's a bug in KSP where, after using KSType#asMemberOf() or KSType#replace(),
+// the annotations are removed from the resulting type. However, it turns out that the annotation
+// information is still available in the underlying KotlinType, so we use reflection to get them.
+// See https://github.com/google/ksp/issues/1376.
+private fun KSType.hasAnnotation(qName: String): Boolean {
+ fun String.toFqName(): Any {
+ return Class.forName("org.jetbrains.kotlin.name.FqName")
+ .getConstructor(String::class.java)
+ .newInstance(this)
+ }
+ fun hasAnnotationViaReflection(qName: String): Boolean {
+ val ksType = if (
+ // Note: Technically, we could just make KSTypeWrapper internal and cast to get the
+ // delegate, but since we need to use reflection anyway, just get it via reflection.
+ this.javaClass.canonicalName == "androidx.room.compiler.processing.ksp.KSTypeWrapper") {
+ this.javaClass.methods.find { it.name == "getDelegate" }?.invoke(this)
+ } else {
+ this
+ }
+ val kotlinType =
+ ksType?.javaClass?.methods?.find { it.name == "getKotlinType" }?.invoke(ksType)
+ val kotlinAnnotations =
+ kotlinType?.javaClass
+ ?.methods
+ ?.find { it.name == "getAnnotations" }
+ ?.invoke(kotlinType)
+ return kotlinAnnotations?.javaClass
+ ?.methods
+ ?.find { it.name == "hasAnnotation" }
+ ?.invoke(kotlinAnnotations, qName.toFqName()) == true
+ }
+ return if (annotations.toList().isEmpty()) {
+ // If there are no annotations but KSType#toString() shows annotations, check the underlying
+ // KotlinType for annotations using reflection.
+ toString().startsWith("[") && hasAnnotationViaReflection(qName)
+ } else {
+ annotations.any { it.annotationType.resolve().hasQualifiedNameOrAlias(qName) }
+ }
+}
internal fun KSType.hasSuppressJvmWildcardAnnotation() =
hasAnnotation(JvmSuppressWildcards::class.java.canonicalName!!)
internal fun KSNode.hasSuppressWildcardsAnnotationInHierarchy(): Boolean {
- (this as? KSAnnotated)?.let {
- if (hasSuppressJvmWildcardAnnotation()) {
- return true
- }
- }
- val parent = parent ?: return false
- return parent.hasSuppressWildcardsAnnotationInHierarchy()
- }
\ No newline at end of file
+ (this as? KSAnnotated)?.let {
+ if (hasSuppressJvmWildcardAnnotation()) {
+ return true
+ }
+ }
+ val parent = parent ?: return false
+ return parent.hasSuppressWildcardsAnnotationInHierarchy()
+}
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt
index bdade61..d797d50 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt
@@ -25,6 +25,7 @@
import com.google.devtools.ksp.symbol.KSTypeAlias
import com.google.devtools.ksp.symbol.KSTypeArgument
import com.google.devtools.ksp.symbol.KSTypeParameter
+import com.google.devtools.ksp.symbol.KSTypeReference
import com.google.devtools.ksp.symbol.Modifier
import com.google.devtools.ksp.symbol.Variance
@@ -59,21 +60,57 @@
return type
}
- val resolvedType = if (hasTypeVariables(scope.declarationType())) {
+ // First, replace any type aliases in the type with their actual types
+ return type.replaceTypeAliases()
+ // Next, resolve wildcards based on the scope of the type
+ .resolveWildcards(scope)
+ // Next, apply any additional variance changes based on the @JvmSuppressWildcards or
+ // @JvmWildcard annotations on the resolved type.
+ .applyJvmWildcardAnnotations()
+ // Finally, unwrap any delegate types. (Note: as part of resolving wildcards, we wrap
+ // types/type arguments in delegates to avoid loosing annotation information. However,
+ // those delegates may cause issues later if KSP tries to cast the type/argument to a
+ // particular implementation, so we unwrap them here.
+ .removeWrappers()
+ }
+
+ private fun KSType.replaceTypeAliases(typeStack: ReferenceStack = ReferenceStack()): KSType {
+ if (isError || typeStack.queue.contains(this)) {
+ return this
+ }
+ if (declaration is KSTypeAlias) {
+ return (declaration as KSTypeAlias).type.resolve().replaceTypeAliases(typeStack)
+ }
+ return typeStack.withReference(this) {
+ createWrapper(arguments.map { it.replaceTypeAliases(typeStack) })
+ }
+ }
+
+ private fun KSTypeArgument.replaceTypeAliases(typeStack: ReferenceStack): KSTypeArgument {
+ val type = type?.resolve()
+ if (
+ type == null ||
+ type.isError ||
+ variance == Variance.STAR ||
+ typeStack.queue.contains(type)
+ ) {
+ return this
+ }
+ return createWrapper(type.replaceTypeAliases(typeStack), variance)
+ }
+
+ private fun KSType.resolveWildcards(scope: KSTypeVarianceResolverScope): KSType {
+ return if (hasTypeVariables(scope.declarationType())) {
// If the associated declared type contains type variables that were resolved, e.g.
// using "asMemberOf", then it has special rules about how to resolve the types.
getJavaWildcardWithTypeVariables(
- type = type,
+ type = this,
declarationType = getJavaWildcard(scope.declarationType(), scope),
scope = scope,
)
} else {
- getJavaWildcard(type, scope)
+ getJavaWildcard(this, scope)
}
-
- // As a final pass, we apply variance from any @JvmSuppressWildcards or @JvmWildcard
- // annotations on the resolved type.
- return applyJvmWildcardAnnotations(resolvedType)
}
private fun hasTypeVariables(
@@ -99,15 +136,6 @@
if (type.isError || typeStack.queue.contains(type)) {
return type
}
- if (type.declaration is KSTypeAlias) {
- return getJavaWildcard(
- type = (type.declaration as KSTypeAlias).type.resolve(),
- scope = scope,
- typeStack = typeStack,
- typeArgStack = typeArgStack,
- typeParamStack = typeParamStack,
- )
- }
return typeStack.withReference(type) {
val resolvedTypeArgs =
type.arguments.indices.map { i ->
@@ -120,7 +148,7 @@
typeParamStack = typeParamStack,
)
}
- type.replace(resolvedTypeArgs)
+ type.createWrapper(resolvedTypeArgs)
}
}
@@ -158,10 +186,10 @@
if (typeParamStack.indices.none { i ->
(typeParamStack[i].variance == Variance.CONTRAVARIANT ||
typeArgStack[i].variance == Variance.CONTRAVARIANT) &&
- // The declaration and use site variance is ignored when using @JvmWildcard
- // explicitly on a type.
- !typeArgStack[i].hasJvmWildcardAnnotation()
- }) {
+ // The declaration and use site variance is ignored when using @JvmWildcard
+ // explicitly on a type.
+ !typeArgStack[i].hasJvmWildcardAnnotation()
+ }) {
return false
}
} else {
@@ -217,7 +245,7 @@
} else {
typeArg.variance
}
- return createTypeArgument(resolvedType, resolvedVariance)
+ return typeArg.createWrapper(resolvedType, resolvedVariance)
}
private fun getJavaWildcardWithTypeVariables(
@@ -252,7 +280,7 @@
)
}
}
- type.replace(resolvedTypeArgs)
+ type.createWrapper(resolvedTypeArgs)
}
}
@@ -293,7 +321,7 @@
} else {
typeArg.variance
}
- return createTypeArgument(resolvedType, resolvedVariance)
+ return typeArg.createWrapper(resolvedType, resolvedVariance)
}
private fun getJavaWildcardWithTypeVariablesForOuterType(
@@ -322,26 +350,27 @@
} else {
typeArg.variance
}
- return createTypeArgument(resolvedType, resolvedVariance)
+ return typeArg.createWrapper(resolvedType, resolvedVariance)
}
- private fun applyJvmWildcardAnnotations(
- type: KSType,
+ private fun KSType.applyJvmWildcardAnnotations(
typeStack: ReferenceStack = ReferenceStack(),
+ typeArgStack: List<KSTypeArgument> = emptyList(),
): KSType {
- if (type.isError || typeStack.queue.contains(type)) {
- return type
+ if (isError || typeStack.queue.contains(this)) {
+ return this
}
- return typeStack.withReference(type) {
+ return typeStack.withReference(this) {
val resolvedTypeArgs =
- type.arguments.indices.map { i ->
+ arguments.indices.map { i ->
applyJvmWildcardAnnotations(
- typeArg = type.arguments[i],
- typeParameter = type.declaration.typeParameters[i],
+ typeArg = arguments[i],
+ typeParameter = declaration.typeParameters[i],
+ typeArgStack = typeArgStack,
typeStack = typeStack,
)
}
- type.replace(resolvedTypeArgs)
+ createWrapper(resolvedTypeArgs)
}
}
@@ -349,6 +378,7 @@
typeArg: KSTypeArgument,
typeParameter: KSTypeParameter,
typeStack: ReferenceStack,
+ typeArgStack: List<KSTypeArgument>,
): KSTypeArgument {
val type = typeArg.type?.resolve()
if (
@@ -359,28 +389,107 @@
) {
return typeArg
}
- val resolvedType = applyJvmWildcardAnnotations(
- type = type,
- typeStack = typeStack,
- )
+ val resolvedType = type.applyJvmWildcardAnnotations(typeStack, typeArgStack + typeArg)
val resolvedVariance = when {
typeParameter.variance == Variance.INVARIANT &&
typeArg.variance != Variance.INVARIANT -> typeArg.variance
typeArg.hasJvmWildcardAnnotation() -> typeParameter.variance
- typeStack.queue.any { it.hasSuppressJvmWildcardAnnotation() } ||
+ // We only need to check the first type in the stack for @JvmSuppressWildcards.
+ // Any other @JvmSuppressWildcards usages will be placed on the type arguments rather
+ // than the types, so no need to check the rest of the types.
+ typeStack.queue.first().hasSuppressJvmWildcardAnnotation() ||
typeArg.hasSuppressWildcardsAnnotationInHierarchy() ||
+ typeArgStack.any { it.hasSuppressJvmWildcardAnnotation() } ||
typeParameter.hasSuppressWildcardsAnnotationInHierarchy() -> Variance.INVARIANT
else -> typeArg.variance
}
- return createTypeArgument(resolvedType, resolvedVariance)
+ return typeArg.createWrapper(resolvedType, resolvedVariance)
}
- private fun KSType.isTypeParameter(): Boolean {
- return createTypeReference().isTypeParameterReference()
+ private fun KSTypeArgument.createWrapper(
+ newType: KSType,
+ newVariance: Variance
+ ): KSTypeArgument {
+ return KSTypeArgumentWrapper(
+ delegate = (this as? KSTypeArgumentWrapper)?.delegate ?: this,
+ type = newType.createTypeReference(),
+ variance = newVariance
+ )
}
- private fun createTypeArgument(type: KSType, variance: Variance): KSTypeArgument {
- return resolver.getTypeArgument(type.createTypeReference(), variance)
+ private fun KSType.createWrapper(newArguments: List<KSTypeArgument>): KSType {
+ return KSTypeWrapper(
+ delegate = (this as? KSTypeWrapper)?.delegate ?: this,
+ arguments = newArguments
+ )
+ }
+
+ private fun KSType.removeWrappers(typeStack: ReferenceStack = ReferenceStack()): KSType {
+ if (isError || typeStack.queue.contains(this)) {
+ return this
+ }
+ return typeStack.withReference(this) {
+ val delegateType = (this as? KSTypeWrapper)?.delegate ?: this
+ delegateType.replace(arguments.map { it.removeWrappers(typeStack) })
+ }
+ }
+
+ private fun KSTypeArgument.removeWrappers(
+ typeStack: ReferenceStack = ReferenceStack()
+ ): KSTypeArgument {
+ val type = type?.resolve()
+ if (
+ type == null ||
+ type.isError ||
+ variance == Variance.STAR ||
+ typeStack.queue.contains(type)
+ ) {
+ return this
+ }
+ return resolver.getTypeArgument(
+ type.removeWrappers(typeStack).createTypeReference(),
+ variance
+ )
+ }
+}
+
+/**
+ * A wrapper for creating a new [KSType] that allows arguments of type [KSTypeArgumentWrapper].
+ *
+ * Note: This wrapper acts similar to [KSType#replace(KSTypeArgument)]. However, we can't call
+ * [KSType#replace(KSTypeArgument)] directly when using [KSTypeArgumentWrapper] or we'll get an
+ * [IllegalStateException] since KSP tries to cast to its own implementation of [KSTypeArgument].
+ */
+private class KSTypeWrapper(
+ val delegate: KSType,
+ override val arguments: List<KSTypeArgument>
+) : KSType by delegate {
+ override fun toString() = if (arguments.isNotEmpty()) {
+ "${delegate.toString().substringBefore("<")}<${arguments.joinToString(",")}>"
+ } else {
+ delegate.toString()
+ }
+}
+
+/**
+ * A wrapper for creating a new [KSTypeArgument] that delegates to the original argument for
+ * annotations.
+ *
+ * Note: This wrapper acts similar to [Resolver#getTypeArgument(KSTypeReference, Variance)].
+ * However, we can't call [Resolver#getTypeArgument(KSTypeReference, Variance)] directly because
+ * we'll lose information about annotations (e.g. `@JvmSuppressWildcards`) that were on the original
+ * type argument.
+ */
+private class KSTypeArgumentWrapper(
+ val delegate: KSTypeArgument,
+ override val type: KSTypeReference,
+ override val variance: Variance,
+) : KSTypeArgument by delegate {
+ override fun toString() = when (variance) {
+ Variance.INVARIANT -> "${type.resolve()}"
+ Variance.CONTRAVARIANT -> "in ${type.resolve()}"
+ Variance.COVARIANT -> "out ${type.resolve()}"
+ Variance.STAR -> "*"
}
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverScope.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverScope.kt
index f39ab11..19f546a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverScope.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverScope.kt
@@ -27,7 +27,7 @@
* Provides KSType resolution scope for a type.
*/
internal sealed class KSTypeVarianceResolverScope(
- private val annotated: KSAnnotated,
+ val annotated: KSAnnotated,
private val container: KSDeclaration?,
private val asMemberOf: KspType?
) {
@@ -36,8 +36,15 @@
* parameter is in kotlin or the containing class, which inherited the method, is in kotlin.
*/
val needsWildcardResolution: Boolean by lazy {
+ fun nodeForSuppressionCheck(): KSAnnotated? = when (this) {
+ // For property setter and getter methods skip to the enclosing class to check for
+ // suppression annotations to match KAPT.
+ is PropertySetterParameterType,
+ is PropertyGetterMethodReturnType -> annotated.parent?.parent as? KSAnnotated
+ else -> annotated
+ }
(annotated.isInKotlinCode() || container?.isInKotlinCode() == true) &&
- !annotated.hasSuppressWildcardsAnnotationInHierarchy()
+ nodeForSuppressionCheck()?.hasSuppressWildcardsAnnotationInHierarchy() != true
}
private fun KSAnnotated.isInKotlinCode(): Boolean {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt
index 21395f6..a2b2895 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt
@@ -66,8 +66,8 @@
@Test
fun outOfOrderJava_fields() {
- val libSource = Source.kotlin(
- "JavaClass.java",
+ val libSource = Source.java(
+ "JavaClass",
"""
class JavaClass {
String b;
@@ -116,8 +116,8 @@
@Test
fun outOfOrderJava_methods() {
- val libSource = Source.kotlin(
- "JavaClass.java",
+ val libSource = Source.java(
+ "JavaClass",
"""
class JavaClass {
String b() { return ""; }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
index ad37a87..7a761a0 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
@@ -436,7 +436,7 @@
""".trimIndent()
)
val javaSrc = Source.java(
- "JavaClass.java",
+ "JavaClass",
"""
import androidx.room.compiler.processing.testcode.JavaAnnotationWithDefaults;
@JavaAnnotationWithDefaults
@@ -499,7 +499,7 @@
@Test
fun javaPrimitiveArray() {
val javaSrc = Source.java(
- "JavaSubject.java",
+ "JavaSubject",
"""
import androidx.room.compiler.processing.testcode.*;
class JavaSubject {
@@ -592,7 +592,7 @@
@Test
fun javaEnum() {
val javaSrc = Source.java(
- "JavaSubject.java",
+ "JavaSubject",
"""
import androidx.room.compiler.processing.testcode.*;
class JavaSubject {
@@ -632,7 +632,7 @@
@Test
fun javaEnumArray() {
val javaSrc = Source.java(
- "JavaSubject.java",
+ "JavaSubject",
"""
import androidx.room.compiler.processing.testcode.*;
class JavaSubject {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
index 973e82a..976ab4f 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
@@ -598,7 +598,7 @@
""".trimIndent()
)
val javaSrc = Source.java(
- "JavaClass.java",
+ "JavaClass",
"""
import androidx.room.compiler.processing.testcode.JavaAnnotationWithDefaults;
@JavaAnnotationWithDefaults
@@ -681,7 +681,7 @@
fun javaPrimitiveArray() {
// TODO: expand this test for other primitive types: 179081610
val javaSrc = Source.java(
- "JavaSubject.java",
+ "JavaSubject",
"""
import androidx.room.compiler.processing.testcode.*;
class JavaSubject {
@@ -720,7 +720,7 @@
@Test
fun javaEnum() {
val javaSrc = Source.java(
- "JavaSubject.java",
+ "JavaSubject",
"""
import androidx.room.compiler.processing.testcode.*;
class JavaSubject {
@@ -759,7 +759,7 @@
@Test
fun javaEnumArray() {
val javaSrc = Source.java(
- "JavaSubject.java",
+ "JavaSubject",
"""
import androidx.room.compiler.processing.testcode.*;
class JavaSubject {
@@ -1151,7 +1151,7 @@
""".trimIndent()
)
val javaSource = Source.java(
- "foo.bar.Subject.java",
+ "foo.bar.Subject",
"""
package foo.bar;
import java.lang.annotation.ElementType;
@@ -1232,7 +1232,7 @@
""".trimIndent()
)
val javaSource = Source.java(
- "foo.bar.Subject.java",
+ "foo.bar.Subject",
"""
package foo.bar;
import java.lang.annotation.ElementType;
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
index bc037bc..4c88042 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
@@ -44,7 +44,7 @@
""".trimIndent()
),
Source.java(
- "JavaClass.java",
+ "JavaClass",
"""
abstract class JavaClass<T> {
JavaClass(T t) {}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt
index 4091a00..c540af4 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt
@@ -31,7 +31,7 @@
runProcessorTest(
sources = listOf(
Source.java(
- "Foo.java",
+ "Foo",
"""
class Foo {}
""".trimIndent()
@@ -56,7 +56,7 @@
runProcessorTest(
sources = listOf(
Source.java(
- "Foo.java",
+ "Foo",
"""
class Foo {}
""".trimIndent()
@@ -80,7 +80,7 @@
runProcessorTest(
sources = listOf(
Source.java(
- "Foo.java",
+ "Foo",
"""
class Foo {}
""".trimIndent()
@@ -104,7 +104,7 @@
runProcessorTest(
sources = listOf(
Source.java(
- "Foo.java",
+ "Foo",
"""
class Foo {}
""".trimIndent()
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
index 4786cff..313e735c 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
@@ -385,7 +385,7 @@
@Test
fun makeNullable_void() {
val src = Source.java(
- "Foo.java",
+ "Foo",
"""
class Foo {
void subject() {}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
index 0d60682..33ff8fc 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
@@ -223,7 +223,7 @@
@Test
fun errorLogFailsCompilation() {
val src = Source.java(
- "Foo.java",
+ "Foo",
"""
class Foo {}
""".trimIndent()
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
index 134a9c2..0e44884 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
@@ -426,7 +426,7 @@
""".trimIndent()
)
val javaSrc = Source.java(
- "Bar.java",
+ "Bar",
"""
class JavaClass {}
interface JavaInterface {}
@@ -728,7 +728,7 @@
runTest(
listOf(
Source.java(
- "JavaSubject.java",
+ "JavaSubject",
"""
class JavaSubject {
int myField;
@@ -752,7 +752,7 @@
)
),
) { invocation ->
- listOf("JavaSubject", "KotlinSubject",).map {
+ listOf("JavaSubject", "KotlinSubject").map {
invocation.processingEnv.requireTypeElement(it)
}.forEach { subject ->
val methods = subject.getDeclaredMethods()
@@ -2075,6 +2075,272 @@
}
}
+ @Test
+ fun javaFieldDescriptors() {
+ runTest(
+ sources = listOf(
+ Source.java(
+ "TestClassA",
+ """
+ import java.util.List;
+ class TestClassA<T> {
+ int field1;
+ String field2;
+ T field3;
+ List<String> field4;
+ }
+ """.trimIndent()
+ )
+ )
+ ) { invocation ->
+ val foo = invocation.processingEnv.requireTypeElement("TestClassA")
+ assertThat(foo.getDeclaredFields().map { it.jvmDescriptor }.toList())
+ .containsExactly(
+ "field1:I",
+ "field2:Ljava/lang/String;",
+ "field3:Ljava/lang/Object;",
+ "field4:Ljava/util/List;"
+ )
+ }
+ }
+
+ @Test
+ fun javaMethodDescriptorsPrimitives() {
+ runTest(
+ sources = listOf(
+ Source.java(
+ "TestClassB",
+ """
+ class TestClassB<T> {
+ void method1(boolean yesOrNo, int number) {}
+
+ byte method2(char letter) {
+ return 0;
+ }
+
+ void method3(double realNumber1, float realNummber2) {}
+
+ void method4(long bigNumber, short littlerNumber) {}
+ }
+ """.trimIndent()
+ )
+ )
+ ) { invocation ->
+ val foo = invocation.processingEnv.requireTypeElement("TestClassB")
+ assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
+ .containsExactly(
+ "method1(ZI)V", "method2(C)B", "method3(DF)V", "method4(JS)V"
+ )
+ }
+ }
+
+ @Test
+ fun javaMethodDescriptorsJavaTypes() {
+ runTest(
+ sources = listOf(
+ Source.java(
+ "TestClassC",
+ """
+ import java.util.*;
+ class TestClassC<T> {
+ void method1(Object something) {}
+
+ Object method2() {
+ return null;
+ }
+
+ List<String> method3(ArrayList<Integer> list) {
+ return null;
+ }
+
+ Map<String, Object> method4() {
+ return null;
+ }
+ }
+ """.trimIndent()
+ )
+ )
+ ) { invocation ->
+ val foo = invocation.processingEnv.requireTypeElement("TestClassC")
+ assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
+ .containsExactly(
+ "method1(Ljava/lang/Object;)V",
+ "method2()Ljava/lang/Object;",
+ "method3(Ljava/util/ArrayList;)Ljava/util/List;",
+ "method4()Ljava/util/Map;"
+ )
+ }
+ }
+
+ @Test
+ fun javaMethodDescriptorsTestTypes() {
+ runTest(
+ sources = listOf(
+ Source.java(
+ "TestClassD",
+ """
+ class TestDataClass {}
+ class TestClassD<T> {
+ void method1(TestDataClass data) {}
+
+ TestDataClass method2() {
+ return null;
+ }
+ }
+ """.trimIndent()
+ )
+ )
+ ) { invocation ->
+ val foo = invocation.processingEnv.requireTypeElement("TestClassD")
+ assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
+ .containsExactly(
+ "method1(LTestDataClass;)V",
+ "method2()LTestDataClass;"
+ )
+ }
+ }
+
+ @Test
+ fun javaMethodDescriptorsArrays() {
+ runTest(
+ sources = listOf(
+ Source.java(
+ "TestClassE",
+ """
+ class TestDataClass {}
+ class TestClassE<T> {
+ void method1(TestDataClass[] data) {}
+
+ TestDataClass[] method2() {
+ return null;
+ }
+
+ void method3(int[] array) {}
+
+ void method4(int... array) {}
+ }
+ """.trimIndent()
+ )
+ )
+ ) { invocation ->
+ val foo = invocation.processingEnv.requireTypeElement("TestClassE")
+ assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
+ .containsExactly(
+ "method1([LTestDataClass;)V",
+ "method2()[LTestDataClass;",
+ "method3([I)V",
+ "method4([I)V"
+ )
+ }
+ }
+
+ @Test
+ fun javaMethodDescriptorsInnerTestType() {
+ runTest(
+ // KSP can't see nested types if the filename does not match the name of the
+ // enclosing class.
+ sources = listOf(
+ Source.java(
+ "TestDataClass",
+ """
+ public class TestDataClass {
+ class MemberInnerData {}
+
+ static class StaticInnerData {}
+
+ enum EnumData {
+ VALUE1,
+ VALUE2
+ }
+ }
+ """.trimIndent()
+ ),
+ Source.java(
+ "TestClassF",
+ """
+ class TestClassF<T> {
+ void method1(TestDataClass.MemberInnerData data) {}
+
+ void method2(TestDataClass.StaticInnerData data) {}
+
+ void method3(TestDataClass.EnumData enumData) {}
+
+ TestDataClass.StaticInnerData method4() {
+ return null;
+ }
+ }
+ """.trimIndent()
+ )
+ )
+ ) { invocation ->
+ val foo = invocation.processingEnv.requireTypeElement("TestClassF")
+ assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
+ .containsExactly(
+ "method1(LTestDataClass\$MemberInnerData;)V",
+ "method2(LTestDataClass\$StaticInnerData;)V",
+ "method3(LTestDataClass\$EnumData;)V",
+ "method4()LTestDataClass\$StaticInnerData;"
+ )
+ }
+ }
+
+ @Test
+ fun methodDescriptorsErasure() {
+ runTest(
+ sources = listOf(
+ Source.java(
+ "TestClassG",
+ """
+ import java.util.*;
+ class TestClassG<T> {
+ void method1(T something) {}
+ T method2() {
+ return null;
+ }
+ List<? extends String> method3() {
+ return null;
+ }
+ Map<T, String> method4() {
+ return null;
+ }
+ ArrayList<Map<T, String>> method5() {
+ return null;
+ }
+ static <I, O extends I> O method6(I input) {
+ return null;
+ }
+ static <I, O extends String> O method7(I input) {
+ return null;
+ }
+ static <P extends Collection<String> & Comparable<String>> P method8() {
+ return null;
+ }
+ static <P extends String & List<Character>> P method9() {
+ return null;
+ }
+ }
+ """.trimIndent()
+ )
+ )
+ ) { invocation ->
+ val foo = invocation.processingEnv.requireTypeElement("TestClassG")
+ if (!invocation.isKsp) {
+ assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList())
+ .containsExactly(
+ "method1(Ljava/lang/Object;)V",
+ "method2()Ljava/lang/Object;",
+ "method3()Ljava/util/List;",
+ "method4()Ljava/util/Map;",
+ "method5()Ljava/util/ArrayList;",
+ "method6(Ljava/lang/Object;)Ljava/lang/Object;",
+ "method7(Ljava/lang/Object;)Ljava/lang/String;",
+ "method8()Ljava/util/Collection;",
+ "method9()Ljava/lang/String;"
+ )
+ }
+ }
+ }
+
/**
* it is good to exclude methods coming from Object when testing as they differ between KSP
* and KAPT but irrelevant for Room.
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
index 2bd5ed4..be3f4bf 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
@@ -159,6 +159,11 @@
class MyGenericIn<in T>
class MyGenericOut<out T>
class MyGenericMultipleParameters<T1: MyGeneric<*>, T2: MyGeneric<T1>>
+ interface MyInterface
+ typealias MyInterfaceAlias = MyInterface
+ typealias MyGenericAlias = MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>
+ typealias JSW = JvmSuppressWildcards
+ typealias JW = JvmWildcard
typealias MyLambdaTypeAlias = (@JvmWildcard MyType) -> @JvmWildcard MyType
enum class MyEnum {
VAL1,
@@ -341,6 +346,14 @@
sealedListChild: List<GrandParentSealed.Parent2.Child1>,
jvmWildcard: List<@JvmWildcard String>,
suppressJvmWildcard: List<@JvmSuppressWildcards Number>,
+ suppressJvmWildcardsGeneric1: @JvmSuppressWildcards List<MyGenericOut<MyGenericIn<MyGeneric<MyType>>>>,
+ suppressJvmWildcardsGeneric2: List<@JvmSuppressWildcards MyGenericOut<MyGenericIn<MyGeneric<MyType>>>>,
+ suppressJvmWildcardsGeneric3: List<MyGenericOut<@JvmSuppressWildcards MyGenericIn<MyGeneric<MyType>>>>,
+ suppressJvmWildcardsGeneric4: List<MyGenericOut<MyGenericIn<@JvmSuppressWildcards MyGeneric<MyType>>>>,
+ interfaceAlias: List<MyInterfaceAlias>,
+ genericAlias: List<MyGenericAlias>,
+ jvmWildcardTypeAlias: List<@JW String>,
+ suppressJvmWildcardTypeAlias: List<@JSW Number>,
) {
var propWithFinalType: String = ""
var propWithOpenType: Number = 3
@@ -353,6 +366,16 @@
var propSealedListChild: List<GrandParentSealed.Parent2.Child1> = TODO()
@JvmSuppressWildcards
var propWithOpenTypeButSuppressAnnotation: Number = 3
+ var genericVar: List<MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>> = TODO()
+ @JvmSuppressWildcards var suppressJvmWildcardsGenericVar1: List<MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>> = TODO()
+ var suppressJvmWildcardsGenericVar2: @JvmSuppressWildcards List<MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>> = TODO()
+ var suppressJvmWildcardsGenericVar3: List<@JvmSuppressWildcards MyGenericIn<MyGenericOut<MyGenericOut<MyType>>>> = TODO()
+ var suppressJvmWildcardsGenericVar4: List<MyGenericIn<@JvmSuppressWildcards MyGenericOut<MyGenericOut<MyType>>>> = TODO()
+ var suppressJvmWildcardsGenericVar5: List<MyGenericIn<MyGenericOut<@JvmSuppressWildcards MyGenericOut<MyType>>>> = TODO()
+ var interfaceAlias: List<MyInterfaceAlias> = TODO()
+ var genericAlias: List<MyGenericAlias> = TODO()
+ var jvmWildcardTypeAlias: List<@JW String> = TODO()
+ var suppressJvmWildcardTypeAlias: List<@JSW Number> = TODO()
fun list(list: List<*>): List<*> { TODO() }
fun listTypeArg(list: List<R>): List<R> { TODO() }
fun listTypeArgNumber(list: List<Number>): List<Number> { TODO() }
@@ -393,6 +416,43 @@
fun suspendExplicitJvmSuppressWildcard_OnType2(
list: @JvmSuppressWildcards List<Number>
): @JvmSuppressWildcards List<Number> { TODO() }
+ fun interfaceAlias(
+ param: List<MyInterfaceAlias>
+ ): List<MyInterfaceAlias> = TODO()
+ fun explicitJvmSuppressWildcardsOnAlias(
+ param: List<@JvmSuppressWildcards MyInterfaceAlias>,
+ ): List<@JvmSuppressWildcards MyInterfaceAlias> = TODO()
+ fun genericAlias(param: List<MyGenericAlias>): List<MyGenericAlias> = TODO()
+ fun explicitJvmSuppressWildcardsOnGenericAlias(
+ param: List<@JvmSuppressWildcards MyGenericAlias>,
+ ): List<@JvmSuppressWildcards MyGenericAlias> = TODO()
+ fun explicitOutOnInvariant_onType1_WithExplicitJvmSuppressWildcardAlias(
+ list: @JSW MyGeneric<out MyGeneric<MyType>>
+ ): @JSW MyGeneric<out MyGeneric<MyType>> { TODO() }
+ fun explicitOutOnInvariant_onType2_WithExplicitJvmSuppressWildcardAlias(
+ list: @JSW MyGeneric<MyGeneric<out MyType>>
+ ): @JSW MyGeneric<MyGeneric<out MyType>> { TODO() }
+ fun explicitOutOnVariant_onType1(
+ list: List<out List<Number>>
+ ): List<out List<Number>> { TODO() }
+ fun explicitOutOnVariant_onType2(
+ list: List<List<out Number>>
+ ): List<List<out Number>> { TODO() }
+ fun explicitOutOnVariant_onType1_WithExplicitJvmSuppressWildcardAlias(
+ list: @JSW List<out List<Number>>
+ ): @JSW List<out List<Number>> { TODO() }
+ fun explicitOutOnVariant_onType2_WithExplicitJvmSuppressWildcardAlias(
+ list: @JSW List<List<out Number>>
+ ): @JSW List<List<out Number>> { TODO() }
+ fun explicitJvmWildcardTypeAlias(
+ list: List<@JW String>
+ ): List<@JW String> { TODO() }
+ fun explicitJvmSuppressWildcardTypeAlias_OnType(
+ list: List<@JSW Number>
+ ): List<@JSW Number> { TODO() }
+ fun explicitJvmSuppressWildcardTypeAlias_OnType2(
+ list: @JSW List<Number>
+ ): @JSW List<Number> { TODO() }
}
""".trimIndent()
), listOf(className)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt b/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt
index 472f46e..62a4e01 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt
@@ -107,6 +107,7 @@
val filename = "${db.version}.json"
val exportToResources =
Context.BooleanProcessorOptions.EXPORT_SCHEMA_RESOURCE.getValue(env)
+ val schemaInFolderPath = context.schemaInFolderPath
val schemaOutFolderPath = context.schemaOutFolderPath
if (exportToResources) {
context.logger.w(ProcessorErrors.EXPORTING_SCHEMA_TO_RESOURCES)
@@ -115,19 +116,24 @@
originatingElements = listOf(db.element)
)
db.exportSchema(schemaFileOutputStream)
- } else if (schemaOutFolderPath != null) {
+ } else if (schemaInFolderPath != null && schemaOutFolderPath != null) {
+ val schemaInFolder = SchemaFileResolver.RESOLVER.getFile(
+ Path.of(schemaInFolderPath)
+ )
val schemaOutFolder = SchemaFileResolver.RESOLVER.getFile(
Path.of(schemaOutFolderPath)
)
if (!schemaOutFolder.exists()) {
schemaOutFolder.mkdirs()
}
- val dbSchemaFolder = File(schemaOutFolder, qName)
- if (!dbSchemaFolder.exists()) {
- dbSchemaFolder.mkdirs()
+ val dbSchemaInFolder = File(schemaInFolder, qName)
+ val dbSchemaOutFolder = File(schemaOutFolder, qName)
+ if (!dbSchemaOutFolder.exists()) {
+ dbSchemaOutFolder.mkdirs()
}
db.exportSchema(
- File(dbSchemaFolder, "${db.version}.json")
+ inputFile = File(dbSchemaInFolder, "${db.version}.json"),
+ outputFile = File(dbSchemaOutFolder, "${db.version}.json")
)
} else {
context.logger.w(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt
index 07ee499..54969bf 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt
@@ -147,10 +147,32 @@
}
}
+ val schemaInFolderPath by lazy {
+ val internalInputFolder =
+ processingEnv.options[ProcessorOptions.INTERNAL_SCHEMA_INPUT_FOLDER.argName]
+ val legacySchemaFolder =
+ processingEnv.options[ProcessorOptions.OPTION_SCHEMA_FOLDER.argName]
+ if (!internalInputFolder.isNullOrBlank()) {
+ internalInputFolder
+ } else if (!legacySchemaFolder.isNullOrBlank()) {
+ legacySchemaFolder
+ } else {
+ null
+ }
+ }
+
val schemaOutFolderPath by lazy {
- val arg = processingEnv.options[ProcessorOptions.OPTION_SCHEMA_FOLDER.argName]
- if (arg?.isNotEmpty() == true) {
- arg
+ val internalOutputFolder =
+ processingEnv.options[ProcessorOptions.INTERNAL_SCHEMA_OUTPUT_FOLDER.argName]
+ val legacySchemaFolder =
+ processingEnv.options[ProcessorOptions.OPTION_SCHEMA_FOLDER.argName]
+ if (!internalOutputFolder.isNullOrBlank() && !legacySchemaFolder.isNullOrBlank()) {
+ logger.e(ProcessorErrors.INVALID_GRADLE_PLUGIN_AND_SCHEMA_LOCATION_OPTION)
+ }
+ if (!internalOutputFolder.isNullOrBlank()) {
+ internalOutputFolder
+ } else if (!legacySchemaFolder.isNullOrBlank()) {
+ legacySchemaFolder
} else {
null
}
@@ -256,7 +278,9 @@
}
enum class ProcessorOptions(val argName: String) {
- OPTION_SCHEMA_FOLDER("room.schemaLocation")
+ OPTION_SCHEMA_FOLDER("room.schemaLocation"),
+ INTERNAL_SCHEMA_INPUT_FOLDER("room.internal.schemaInput"),
+ INTERNAL_SCHEMA_OUTPUT_FOLDER("room.internal.schemaOutput"),
}
enum class BooleanProcessorOptions(val argName: String, private val defaultValue: Boolean) {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt
index 6e045f2..211cd37 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt
@@ -28,7 +28,7 @@
import androidx.room.migration.bundle.DatabaseBundle
import androidx.room.migration.bundle.SchemaBundle
import androidx.room.processor.ProcessorErrors.AUTO_MIGRATION_FOUND_BUT_EXPORT_SCHEMA_OFF
-import androidx.room.processor.ProcessorErrors.AUTO_MIGRATION_SCHEMA_OUT_FOLDER_NULL
+import androidx.room.processor.ProcessorErrors.AUTO_MIGRATION_SCHEMA_IN_FOLDER_NULL
import androidx.room.processor.ProcessorErrors.autoMigrationSchemasMustBeRoomGenerated
import androidx.room.processor.ProcessorErrors.invalidAutoMigrationSchema
import androidx.room.util.SchemaFileResolver
@@ -151,63 +151,65 @@
val autoMigrationList = dbAnnotation
.getAsAnnotationBoxArray<AutoMigration>("autoMigrations")
+ if (autoMigrationList.isEmpty()) {
+ return emptyList()
+ }
- if (autoMigrationList.isNotEmpty()) {
- if (!dbAnnotation.value.exportSchema) {
- context.logger.e(
- element,
- AUTO_MIGRATION_FOUND_BUT_EXPORT_SCHEMA_OFF
- )
- return emptyList()
- }
- if (context.schemaOutFolderPath == null) {
- context.logger.e(
- element,
- AUTO_MIGRATION_SCHEMA_OUT_FOLDER_NULL
- )
- return emptyList()
- }
+ if (!dbAnnotation.value.exportSchema) {
+ context.logger.e(
+ element,
+ AUTO_MIGRATION_FOUND_BUT_EXPORT_SCHEMA_OFF
+ )
+ return emptyList()
+ }
+ val schemaInFolderPath = context.schemaInFolderPath
+ if (schemaInFolderPath == null) {
+ context.logger.e(
+ element,
+ AUTO_MIGRATION_SCHEMA_IN_FOLDER_NULL
+ )
+ return emptyList()
}
return autoMigrationList.mapNotNull {
- val databaseSchemaFolderPath = Path.of(
- context.schemaOutFolderPath!!,
+ val databaseSchemaInFolderPath = Path.of(
+ schemaInFolderPath,
element.asClassName().canonicalName
)
val autoMigration = it.value
val validatedFromSchemaFile = getValidatedSchemaFile(
autoMigration.from,
- databaseSchemaFolderPath
- )
+ databaseSchemaInFolderPath
+ ) ?: return@mapNotNull null
- fun deserializeSchemaFile(fileInputStream: FileInputStream, versionNumber: Int): Any {
+ fun deserializeSchemaFile(
+ fileInputStream: FileInputStream,
+ versionNumber: Int
+ ): DatabaseBundle? {
return try {
SchemaBundle.deserialize(fileInputStream).database
} catch (th: Throwable) {
invalidAutoMigrationSchema(
"$versionNumber.json",
- databaseSchemaFolderPath.toString()
+ databaseSchemaInFolderPath.toString()
)
+ null
}
}
- if (validatedFromSchemaFile != null) {
- val fromSchemaBundle = validatedFromSchemaFile.inputStream().use {
- deserializeSchemaFile(it, autoMigration.from)
- }
- val toSchemaBundle = if (autoMigration.to == latestDbSchema.version) {
+ val fromSchemaBundle = validatedFromSchemaFile.inputStream().use {
+ deserializeSchemaFile(it, autoMigration.from)
+ }
+ val toSchemaBundle =
+ if (autoMigration.to == latestDbSchema.version) {
latestDbSchema
} else {
val validatedToSchemaFile = getValidatedSchemaFile(
autoMigration.to,
- databaseSchemaFolderPath
- )
- if (validatedToSchemaFile != null) {
- validatedToSchemaFile.inputStream().use {
- deserializeSchemaFile(it, autoMigration.to)
- }
- } else {
- return@mapNotNull null
+ databaseSchemaInFolderPath
+ ) ?: return@mapNotNull null
+ validatedToSchemaFile.inputStream().use {
+ deserializeSchemaFile(it, autoMigration.to)
}
}
if (fromSchemaBundle !is DatabaseBundle || toSchemaBundle !is DatabaseBundle) {
@@ -221,15 +223,12 @@
return@mapNotNull null
}
- AutoMigrationProcessor(
- context = context,
- spec = it.getAsType("spec")!!,
- fromSchemaBundle = fromSchemaBundle,
- toSchemaBundle = toSchemaBundle
- ).process()
- } else {
- null
- }
+ AutoMigrationProcessor(
+ context = context,
+ spec = it.getAsType("spec")!!,
+ fromSchemaBundle = fromSchemaBundle,
+ toSchemaBundle = toSchemaBundle
+ ).process()
}
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
index 0367eeb..1612b0c 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
@@ -539,9 +539,10 @@
""".trim()
}
- val MISSING_SCHEMA_EXPORT_DIRECTORY = "Schema export directory is not provided to the" +
- " annotation processor so we cannot export the schema. You can either provide" +
- " `room.schemaLocation` annotation processor argument OR set exportSchema to false."
+ val MISSING_SCHEMA_EXPORT_DIRECTORY = "Schema export directory was not provided to the" +
+ " annotation processor so Room cannot export the schema. You can either provide" +
+ " `room.schemaLocation` annotation processor argument by applying the Room Gradle plugin" +
+ " (id 'androidx.room') OR set exportSchema to false."
val INVALID_FOREIGN_KEY_ACTION = "Invalid foreign key action. It must be one of the constants" +
" defined in ForeignKey.Action"
@@ -938,8 +939,11 @@
fun invalidAutoMigrationSchema(schemaFile: String, schemaOutFolderPath: String): String {
return "Found invalid schema file '$schemaFile.json' at the schema out " +
- "folder: $schemaOutFolderPath. The schema files must be generated by Room. Cannot " +
- "generate auto migrations."
+ "folder: $schemaOutFolderPath.\nIf you've modified the file, you might've broken the " +
+ "JSON format, try deleting the file and re-running the compiler.\n" +
+ "If you've not modified the file, please file a bug at " +
+ "https://issuetracker.google.com/issues/new?component=413107&template=1096568 " +
+ "with a sample app to reproduce the issue."
}
fun autoMigrationSchemasMustBeRoomGenerated(
@@ -1076,13 +1080,13 @@
return "Conflicting @RenameColumn annotations found: [$annotations]"
}
- val AUTO_MIGRATION_FOUND_BUT_EXPORT_SCHEMA_OFF = "Cannot create auto migrations when export " +
- "schema is OFF."
+ val AUTO_MIGRATION_FOUND_BUT_EXPORT_SCHEMA_OFF = "Cannot create auto migrations when " +
+ "exportSchema is false."
- val AUTO_MIGRATION_SCHEMA_OUT_FOLDER_NULL = "Schema export directory is not provided to the" +
- " annotation processor so we cannot import the schema. To generate auto migrations, you " +
- "must provide `room.schemaLocation` annotation processor argument AND set exportSchema to" +
- " true."
+ val AUTO_MIGRATION_SCHEMA_IN_FOLDER_NULL = "Schema import directory was not provided to the" +
+ " annotation processor so Room cannot read older schemas. To generate auto migrations," +
+ " you must provide `room.schemaLocation` annotation processor arguments by applying the" +
+ " Room Gradle plugin (id 'androidx.room') AND set exportSchema to true."
fun tableWithConflictingPrefixFound(tableName: String): String {
return "The new version of the schema contains '$tableName' a table name" +
@@ -1157,4 +1161,10 @@
" the schema file and extracting it from the JAR but not for production builds, otherwise" +
" the schema file will end up in the final artifact which is typically not desired. This" +
" warning serves as a reminder to use room.exportSchemaResource cautiously."
+
+ val INVALID_GRADLE_PLUGIN_AND_SCHEMA_LOCATION_OPTION = "The Room Gradle plugin " +
+ "(id 'androidx.room') cannot be used with an explicit use of the annotation processor" +
+ "option `room.schemaLocation`, please remove the configuration of the option and " +
+ "configure the schema location via the plugin project extension: " +
+ "`room { schemaDirectory(...) }`."
}
\ No newline at end of file
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/util/SchemaFileResolver.kt b/room/room-compiler/src/main/kotlin/androidx/room/util/SchemaFileResolver.kt
index b97d038..6cd884e 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/util/SchemaFileResolver.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/util/SchemaFileResolver.kt
@@ -28,8 +28,8 @@
/**
* Resolves the given path to a file. The path will be a either a sibling of Room's schema
- * location or the folder itself as provided via the annotation processor option
- * 'room.schemaLocation'.
+ * location or the folder itself as provided via the annotation processor options
+ * 'room.schemaLocation' or 'roomSchemaInput.
*/
fun getFile(path: Path): File
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/Database.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/Database.kt
index 1bcd843b..6674a85 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/Database.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/Database.kt
@@ -101,32 +101,26 @@
DigestUtils.md5Hex(input)
}
- fun exportSchema(file: File) {
+ // Writes scheme file to output file, using the input file to check if the schema has changed
+ // otherwise it is not written.
+ fun exportSchema(inputFile: File, outputFile: File) {
val schemaBundle = SchemaBundle(SchemaBundle.LATEST_FORMAT, bundle)
- if (file.exists()) {
- val existing = try {
- file.inputStream().use {
- SchemaBundle.deserialize(it)
- }
- } catch (th: Throwable) {
- throw IllegalStateException(
- """
- Cannot parse existing schema file: ${file.absolutePath}.
- If you've modified the file, you might've broken the JSON format, try
- deleting the file and re-running the compiler.
- If you've not modified the file, please file a bug at
- https://issuetracker.google.com/issues/new?component=413107&template=1096568
- with a sample app to reproduce the issue.
- """.trimIndent()
- )
+ if (inputFile.exists()) {
+ val existing = inputFile.inputStream().use {
+ SchemaBundle.deserialize(it)
}
+ // If existing schema file is the same as the current schema then do not write the file
+ // which helps the copy task configured by the Room Gradle Plugin skip execution due
+ // to empty variant schema output directory.
if (existing.isSchemaEqual(schemaBundle)) {
return
}
}
- SchemaBundle.serialize(schemaBundle, file)
+ SchemaBundle.serialize(schemaBundle, outputFile)
}
+ // Writes scheme file to output stream, the stream should be for a resource otherwise use the
+ // file version of `exportSchema`.
fun exportSchema(outputStream: OutputStream) {
val schemaBundle = SchemaBundle(SchemaBundle.LATEST_FORMAT, bundle)
SchemaBundle.serialize(schemaBundle, outputStream)
diff --git a/room/room-gradle-plugin/build.gradle b/room/room-gradle-plugin/build.gradle
new file mode 100644
index 0000000..8b12c66
--- /dev/null
+++ b/room/room-gradle-plugin/build.gradle
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+import androidx.build.LibraryType
+import androidx.build.SdkResourceGenerator
+
+plugins {
+ id("AndroidXPlugin")
+ id("kotlin")
+ id("java-gradle-plugin")
+}
+
+configurations {
+ // Config for plugin classpath to be used during tests
+ testPlugin {
+ canBeConsumed = false
+ canBeResolved = true
+ }
+}
+
+dependencies {
+ implementation(libs.kotlinStdlib)
+ implementation(gradleApi())
+ implementation("com.android.tools.build:gradle:7.3.0")
+ compileOnly(libs.kotlinGradlePluginz)
+ compileOnly(libs.kspGradlePluginz)
+
+ testImplementation(project(":internal-testutils-gradle-plugin"))
+ testImplementation(gradleTestKit())
+ testImplementation(libs.junit)
+ testImplementation(libs.truth)
+ testImplementation(libs.testParameterInjector)
+
+ testPlugin(libs.kotlinGradlePluginz)
+ testPlugin(libs.kspGradlePluginz)
+}
+
+SdkResourceGenerator.generateForHostTest(project)
+
+// Configure the generating task of plugin-under-test-metadata.properties to
+// include additional dependencies for the injected plugin classpath that
+// are not present in the main runtime dependencies. This allows us to test
+// the KAPT / KSP plugins while keeping a compileOnly dep on the main source.
+tasks.withType(PluginUnderTestMetadata.class).named("pluginUnderTestMetadata").configure {
+ it.pluginClasspath.from(configurations.testPlugin)
+}
+
+// Configure publishing tasks to be dependencies of 'test' so those artifacts are available for
+// the test project executed with Gradle Test Kit.
+tasks.findByPath("test").dependsOn(
+ tasks.findByPath(":annotation:annotation-experimental:publish"),
+ tasks.findByPath(":room:room-common:publish"),
+ tasks.findByPath(":room:room-runtime:publish"),
+ tasks.findByPath(":room:room-migration:publish"),
+ tasks.findByPath(":room:room-compiler:publish"),
+ tasks.findByPath(":room:room-compiler-processing:publish"),
+ tasks.findByPath(":sqlite:sqlite:publish"),
+ tasks.findByPath(":sqlite:sqlite-framework:publish"),
+)
+
+gradlePlugin {
+ plugins {
+ room {
+ id = "androidx.room"
+ implementationClass = "androidx.room.gradle.RoomGradlePlugin"
+ }
+ }
+}
+
+androidx {
+ name = "Android Room Gradle Plugin"
+ type = LibraryType.GRADLE_PLUGIN
+ inceptionYear = "2023"
+ description = "Android Room Gradle Plugin"
+}
+
+validatePlugins {
+ enableStricterValidation = true
+}
\ No newline at end of file
diff --git a/room/room-gradle-plugin/src/main/java/androidx/room/gradle/RoomExtension.kt b/room/room-gradle-plugin/src/main/java/androidx/room/gradle/RoomExtension.kt
new file mode 100644
index 0000000..dc10b3c
--- /dev/null
+++ b/room/room-gradle-plugin/src/main/java/androidx/room/gradle/RoomExtension.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.room.gradle
+
+import javax.inject.Inject
+import org.gradle.api.provider.Provider
+import org.gradle.api.provider.ProviderFactory
+
+open class RoomExtension @Inject constructor(private val providers: ProviderFactory) {
+ internal var schemaDirectory: Provider<String>? = null
+
+ // TODO(b/279748243): Consider adding overload that takes `org.gradle.api.file.Director`.
+
+ /**
+ * Sets the schema location where Room will output exported schema files.
+ *
+ * The location specified will be used as the base directory for schema files that will be
+ * generated per build variant. i.e. for a 'debug' build of the product flavor 'free' then a
+ * schema will be generated in
+ * `<schemaDirectory>/freeDebug/<database-package>/<database-version>.json`.
+ *
+ * See [Export Schemas Documentation](https://developer.android.com/training/data-storage/room/migrating-db-versions#export-schemas)
+ */
+ open fun schemaDirectory(path: String) {
+ schemaDirectory(providers.provider { path })
+ }
+
+ /**
+ * Sets the schema location where Room will output exported schema files.
+ *
+ * The location specified will be used as the base directory for schema files that will be
+ * generated per build variant. i.e. for a 'debug' build of the product flavor 'free' then a
+ * schema will be generated in
+ * `<schemaDirectory>/freeDebug/<database-package>/<database-version>.json`.
+ *
+ * See [Export Schemas Documentation](https://developer.android.com/training/data-storage/room/migrating-db-versions#export-schemas)
+ */
+ open fun schemaDirectory(path: Provider<String>) {
+ schemaDirectory = path
+ }
+}
\ No newline at end of file
diff --git a/room/room-gradle-plugin/src/main/java/androidx/room/gradle/RoomGradlePlugin.kt b/room/room-gradle-plugin/src/main/java/androidx/room/gradle/RoomGradlePlugin.kt
new file mode 100644
index 0000000..9dacb6d
--- /dev/null
+++ b/room/room-gradle-plugin/src/main/java/androidx/room/gradle/RoomGradlePlugin.kt
@@ -0,0 +1,301 @@
+/*
+ * 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.room.gradle
+
+import com.android.build.api.AndroidPluginVersion
+import com.android.build.api.variant.AndroidComponentsExtension
+import com.android.build.api.variant.ComponentIdentity
+import com.android.build.api.variant.Variant
+import com.android.build.gradle.api.AndroidBasePlugin
+import com.google.devtools.ksp.gradle.KspTaskJvm
+import java.util.Locale
+import javax.inject.Inject
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.contract
+import kotlin.io.path.Path
+import kotlin.io.path.notExists
+import org.gradle.api.DefaultTask
+import org.gradle.api.GradleException
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.Directory
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.file.ProjectLayout
+import org.gradle.api.model.ObjectFactory
+import org.gradle.api.provider.Provider
+import org.gradle.api.tasks.IgnoreEmptyDirectories
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
+import org.gradle.api.tasks.SkipWhenEmpty
+import org.gradle.api.tasks.TaskAction
+import org.gradle.api.tasks.TaskProvider
+import org.gradle.api.tasks.compile.JavaCompile
+import org.gradle.configurationcache.extensions.capitalized
+import org.gradle.process.CommandLineArgumentProvider
+import org.gradle.work.DisableCachingByDefault
+import org.jetbrains.kotlin.gradle.internal.KaptTask
+
+class RoomGradlePlugin @Inject constructor(
+ private val projectLayout: ProjectLayout,
+ private val objectFactory: ObjectFactory,
+) : Plugin<Project> {
+ override fun apply(project: Project) {
+ var configured = false
+ project.plugins.withType(AndroidBasePlugin::class.java) {
+ configured = true
+ configureRoom(project)
+ }
+ project.afterEvaluate {
+ project.check(configured) {
+ "The Room Gradle plugin can only be applied to an Android project."
+ }
+ }
+ }
+
+ private fun configureRoom(project: Project) {
+ // TODO(b/277899741): Validate version of Room supports the AP options configured by plugin.
+ val roomExtension =
+ project.extensions.create("room", RoomExtension::class.java)
+ val componentsExtension =
+ project.extensions.findByType(AndroidComponentsExtension::class.java)
+ project.check(componentsExtension != null) {
+ "Could not find the Android Gradle Plugin (AGP) extension, the Room Gradle plugin " +
+ "should be only applied to an Android projects."
+ }
+ project.check(componentsExtension.pluginVersion >= AndroidPluginVersion(7, 3)) {
+ "The Room Gradle plugin is only compatible with Android Gradle plugin (AGP) " +
+ "version 7.3.0 or higher (found ${componentsExtension.pluginVersion})."
+ }
+ componentsExtension.onVariants { variant ->
+ val locationProvider = roomExtension.schemaDirectory
+ project.check(locationProvider != null) {
+ "The Room Gradle plugin was applied but not schema location was specified. " +
+ "Use the `room { schemaDirectory(...) }` DSL to specify one."
+ }
+ val schemaDirectory = locationProvider.get()
+ project.check(schemaDirectory.isNotEmpty()) {
+ "The schemaDirectory path must not be empty."
+ }
+ configureVariant(project, schemaDirectory, variant)
+ }
+ }
+
+ private fun configureVariant(
+ project: Project,
+ schemaDirectory: String,
+ variant: Variant
+ ) {
+ val androidVariantTaskNames = AndroidVariantsTaskNames(variant.name, variant)
+ val configureTask: (Task, ComponentIdentity) -> RoomSchemaDirectoryArgumentProvider = {
+ task, variantIdentity ->
+ val schemaDirectoryPath = Path(schemaDirectory, variantIdentity.name)
+ if (schemaDirectoryPath.notExists()) {
+ project.check(schemaDirectoryPath.toFile().mkdirs()) {
+ "Unable to create directory: $schemaDirectoryPath"
+ }
+ }
+ val schemaInputDir = objectFactory.directoryProperty().apply {
+ set(project.file(schemaDirectoryPath))
+ }
+
+ val schemaOutputDir =
+ projectLayout.buildDirectory.dir("intermediates/room/schemas/${task.name}")
+
+ val copyTask = androidVariantTaskNames.copyTasks.getOrPut(variant.name) {
+ project.tasks.register(
+ "copyRoomSchemas${variantIdentity.name.capitalize()}",
+ RoomSchemaCopyTask::class.java
+ ) {
+ it.schemaDirectory.set(schemaInputDir)
+ }
+ }
+ copyTask.configure { it.variantSchemaOutputDirectories.from(schemaOutputDir) }
+ task.finalizedBy(copyTask)
+
+ RoomSchemaDirectoryArgumentProvider(
+ forKsp = task.isKspTask(),
+ schemaInputDir = schemaInputDir,
+ schemaOutputDir = schemaOutputDir
+ )
+ }
+
+ configureJavaTasks(project, androidVariantTaskNames, configureTask)
+ configureKaptTasks(project, androidVariantTaskNames, configureTask)
+ configureKspTasks(project, androidVariantTaskNames, configureTask)
+
+ // TODO: Consider also setting up the androidTest and test source set to include the
+ // relevant schema location so users can use MigrationTestHelper without additional
+ // configuration.
+ }
+
+ private fun configureJavaTasks(
+ project: Project,
+ androidVariantsTaskNames: AndroidVariantsTaskNames,
+ configureBlock: (Task, ComponentIdentity) -> RoomSchemaDirectoryArgumentProvider
+ ) = project.tasks.withType(JavaCompile::class.java) { task ->
+ androidVariantsTaskNames.withJavaCompile(task.name)?.let { variantIdentity ->
+ val argProvider = configureBlock.invoke(task, variantIdentity)
+ task.options.compilerArgumentProviders.add(argProvider)
+ }
+ }
+
+ private fun configureKaptTasks(
+ project: Project,
+ androidVariantsTaskNames: AndroidVariantsTaskNames,
+ configureBlock: (Task, ComponentIdentity) -> RoomSchemaDirectoryArgumentProvider
+ ) = project.plugins.withId("kotlin-kapt") {
+ project.tasks.withType(KaptTask::class.java) { task ->
+ androidVariantsTaskNames.withKaptTask(task.name)?.let { variantIdentity ->
+ val argProvider = configureBlock.invoke(task, variantIdentity)
+ // TODO: Update once KT-58009 is fixed.
+ try {
+ // Because of KT-58009, we need to add a `listOf(argProvider)` instead
+ // of `argProvider`.
+ task.annotationProcessorOptionProviders.add(listOf(argProvider))
+ } catch (e: Throwable) {
+ // Once KT-58009 is fixed, adding `listOf(argProvider)` will fail, we will
+ // pass `argProvider` instead, which is the correct way.
+ task.annotationProcessorOptionProviders.add(argProvider)
+ }
+ }
+ }
+ }
+
+ private fun configureKspTasks(
+ project: Project,
+ androidVariantsTaskNames: AndroidVariantsTaskNames,
+ configureBlock: (Task, ComponentIdentity) -> RoomSchemaDirectoryArgumentProvider
+ ) = project.plugins.withId("com.google.devtools.ksp") {
+ project.tasks.withType(KspTaskJvm::class.java) { task ->
+ androidVariantsTaskNames.withKspTaskJvm(task.name)?.let { variantIdentity ->
+ val argProvider = configureBlock.invoke(task, variantIdentity)
+ task.commandLineArgumentProviders.add(argProvider)
+ }
+ }
+ }
+
+ internal class AndroidVariantsTaskNames(
+ private val variantName: String,
+ private val variantIdentity: ComponentIdentity
+ ) {
+ // Variant name to copy task
+ val copyTasks = mutableMapOf<String, TaskProvider<RoomSchemaCopyTask>>()
+
+ private val javaCompileName by lazy {
+ "compile${variantName.capitalized()}JavaWithJavac"
+ }
+
+ private val kaptTaskName by lazy {
+ "kapt${variantName.capitalized()}Kotlin"
+ }
+
+ private val kspTaskJvm by lazy {
+ "ksp${variantName.capitalized()}Kotlin"
+ }
+
+ fun withJavaCompile(taskName: String) =
+ if (taskName == javaCompileName) variantIdentity else null
+
+ fun withKaptTask(taskName: String) =
+ if (taskName == kaptTaskName) variantIdentity else null
+
+ fun withKspTaskJvm(taskName: String) =
+ if (taskName == kspTaskJvm) variantIdentity else null
+ }
+
+ @DisableCachingByDefault(because = "Simple disk bound task.")
+ abstract class RoomSchemaCopyTask : DefaultTask() {
+ @get:InputFiles
+ @get:SkipWhenEmpty
+ @get:IgnoreEmptyDirectories
+ @get:PathSensitive(PathSensitivity.RELATIVE)
+ abstract val variantSchemaOutputDirectories: ConfigurableFileCollection
+
+ @get:Internal
+ abstract val schemaDirectory: DirectoryProperty
+
+ @TaskAction
+ fun copySchemas() {
+ variantSchemaOutputDirectories.files
+ .filter { it.exists() }
+ .forEach {
+ // TODO(b/278266663): Error when two same relative path schemas are found in out
+ // dirs and their content is different an indicator of an inconsistency between
+ // the compile tasks of the same variant.
+ it.copyRecursively(schemaDirectory.get().asFile, overwrite = true)
+ }
+ }
+ }
+
+ class RoomSchemaDirectoryArgumentProvider(
+ @get:Input
+ val forKsp: Boolean,
+ @get:InputFiles
+ @get:PathSensitive(PathSensitivity.RELATIVE)
+ val schemaInputDir: Provider<Directory>,
+ @get:OutputDirectory
+ val schemaOutputDir: Provider<Directory>
+ ) : CommandLineArgumentProvider {
+ override fun asArguments() = buildList {
+ val prefix = if (forKsp) "" else "-A"
+ add("${prefix}room.internal.schemaInput=${schemaInputDir.get().asFile.path}")
+ add("${prefix}room.internal.schemaOutput=${schemaOutputDir.get().asFile.path}")
+ }
+ }
+
+ companion object {
+ internal fun String.capitalize(): String = this.replaceFirstChar {
+ if (it.isLowerCase()) it.titlecase(Locale.US) else it.toString()
+ }
+
+ internal fun Task.isKspTask(): Boolean = try {
+ val kspTaskClass = Class.forName("com.google.devtools.ksp.gradle.KspTask")
+ kspTaskClass.isAssignableFrom(this::class.java)
+ } catch (ex: ClassNotFoundException) {
+ false
+ }
+
+ @OptIn(ExperimentalContracts::class)
+ internal fun Project.check(value: Boolean, lazyMessage: () -> String) {
+ contract {
+ returns() implies value
+ }
+ if (isGradleSyncRunning()) return
+ if (!value) {
+ throw GradleException(lazyMessage())
+ }
+ }
+
+ private fun Project.isGradleSyncRunning() = gradleSyncProps.any {
+ it in this.properties && this.properties[it].toString().toBoolean()
+ }
+
+ private val gradleSyncProps by lazy {
+ listOf(
+ "android.injected.build.model.v2",
+ "android.injected.build.model.only",
+ "android.injected.build.model.only.advanced",
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/room/room-gradle-plugin/src/test/java/androidx/room/gradle/RoomGradlePluginTest.kt b/room/room-gradle-plugin/src/test/java/androidx/room/gradle/RoomGradlePluginTest.kt
new file mode 100644
index 0000000..ce82693
--- /dev/null
+++ b/room/room-gradle-plugin/src/test/java/androidx/room/gradle/RoomGradlePluginTest.kt
@@ -0,0 +1,356 @@
+/*
+ * 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.room.gradle
+
+import androidx.testutils.gradle.ProjectSetupRule
+import com.google.common.truth.Truth.assertThat
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import java.io.File
+import org.gradle.testkit.runner.BuildResult
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(TestParameterInjector::class)
+class RoomGradlePluginTest(
+ @TestParameter val backend: ProcessingBackend
+) {
+ @get:Rule
+ val projectSetup = ProjectSetupRule()
+
+ private val roomVersion by lazy {
+ projectSetup.getLibraryLatestVersionInLocalRepo("androidx/room/room-compiler")
+ }
+
+ private fun setup(projectName: String, projectRoot: File = projectSetup.rootDir) {
+ // copy test project
+ File("src/test/test-data/$projectName").copyRecursively(projectRoot)
+
+ if (backend.isForKotlin) {
+ // copy Kotlin database file
+ File("src/test/test-data/kotlin/MyDatabase.kt").let {
+ it.copyTo(projectRoot.resolve("src/main/java/room/testapp/${it.name}"))
+ }
+ } else {
+ // copy Java database file
+ File("src/test/test-data/java/MyDatabase.java").let {
+ it.copyTo(projectRoot.resolve("src/main/java/room/testapp/${it.name}"))
+ }
+ }
+
+ val additionalPluginsBlock = when (backend) {
+ ProcessingBackend.JAVAC ->
+ ""
+ ProcessingBackend.KAPT ->
+ """
+ id('kotlin-android')
+ id('kotlin-kapt')
+ """
+ ProcessingBackend.KSP ->
+ """
+ id('kotlin-android')
+ id('com.google.devtools.ksp')
+ """
+ }
+
+ val repositoriesBlock = buildString {
+ appendLine("repositories {")
+ projectSetup.allRepositoryPaths.forEach {
+ appendLine("""maven { url "$it" }""")
+ }
+ appendLine("}")
+ }
+
+ val processorConfig = when (backend) {
+ ProcessingBackend.JAVAC -> "annotationProcessor"
+ ProcessingBackend.KAPT -> "kapt"
+ ProcessingBackend.KSP -> "ksp"
+ }
+
+ val kotlinJvmTargetBlock = if (backend.isForKotlin) {
+ """
+ tasks.withType(
+ org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+ ).configureEach {
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+ }
+ """.trimIndent()
+ } else {
+ ""
+ }
+
+ // set up build file
+ File(projectRoot, "build.gradle").writeText(
+ """
+ plugins {
+ id('com.android.application')
+ id('androidx.room')
+ $additionalPluginsBlock
+ }
+
+ $repositoriesBlock
+
+ %s
+
+ dependencies {
+ // Uses latest Room built from tip of tree
+ implementation "androidx.room:room-runtime:$roomVersion"
+ $processorConfig "androidx.room:room-compiler:$roomVersion"
+ }
+
+ android {
+ namespace "room.testapp"
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+ }
+
+ $kotlinJvmTargetBlock
+
+ room {
+ schemaDirectory("${'$'}projectDir/schemas")
+ }
+
+ """
+ .trimMargin()
+ // doing format instead of "$projectSetup.androidProject" on purpose,
+ // because otherwise trimIndent will mess with formatting
+ .format(projectSetup.androidProject)
+
+ )
+ }
+
+ @Test
+ fun testWorkflow() {
+ setup("simple-project")
+
+ // First clean build, all tasks need to run
+ runGradleTasks(CLEAN_TASK, COMPILE_TASK).let { result ->
+ result.assertTaskOutcome(COMPILE_TASK, TaskOutcome.SUCCESS)
+ result.assertTaskOutcome(COPY_TASK, TaskOutcome.SUCCESS)
+ }
+
+ // Schema file at version 1 is created
+ var schemaOneTimestamp: Long
+ projectSetup.rootDir.resolve("schemas/debug/room.testapp.MyDatabase/1.json").let {
+ assertThat(it.exists()).isTrue()
+ schemaOneTimestamp = it.lastModified()
+ }
+
+ // Incremental build, compile task re-runs because schema 1 is used as input, but no copy
+ // is done since schema has not changed.
+ runGradleTasks(COMPILE_TASK).let { result ->
+ result.assertTaskOutcome(COMPILE_TASK, TaskOutcome.SUCCESS)
+ result.assertTaskOutcome(COPY_TASK, TaskOutcome.NO_SOURCE)
+ }
+
+ // Incremental build, everything is up to date.
+ runGradleTasks(COMPILE_TASK).let { result ->
+ result.assertTaskOutcome(COMPILE_TASK, TaskOutcome.UP_TO_DATE)
+ result.assertTaskOutcome(COPY_TASK, TaskOutcome.NO_SOURCE)
+ }
+
+ // Make a change that changes the schema at version 1
+ searchAndReplace(
+ file = projectSetup.rootDir.resolve("src/main/java/room/testapp/MyEntity.java"),
+ search = "// Insert-change",
+ replace = "public String text;"
+ )
+
+ // Incremental build, new schema for version 1 is generated and copied.
+ runGradleTasks(COMPILE_TASK).let { result ->
+ result.assertTaskOutcome(COMPILE_TASK, TaskOutcome.SUCCESS)
+ result.assertTaskOutcome(COPY_TASK, TaskOutcome.SUCCESS)
+ }
+
+ // Check schema file at version 1 is updated
+ projectSetup.rootDir.resolve("schemas/debug/room.testapp.MyDatabase/1.json").let {
+ assertThat(it.exists()).isTrue()
+ assertThat(schemaOneTimestamp).isNotEqualTo(it.lastModified())
+ schemaOneTimestamp = it.lastModified()
+ }
+
+ // Incremental build, compile task re-runs because schema 1 is used as input (it changed),
+ // but no copy is done since schema has not changed.
+ runGradleTasks(COMPILE_TASK).let { result ->
+ result.assertTaskOutcome(COMPILE_TASK, TaskOutcome.SUCCESS)
+ result.assertTaskOutcome(COPY_TASK, TaskOutcome.NO_SOURCE)
+ }
+
+ // Incremental build, everything is up to date.
+ runGradleTasks(COMPILE_TASK).let { result ->
+ result.assertTaskOutcome(COMPILE_TASK, TaskOutcome.UP_TO_DATE)
+ result.assertTaskOutcome(COPY_TASK, TaskOutcome.NO_SOURCE)
+ }
+
+ // Add a new file, it does not change the schema
+ projectSetup.rootDir.resolve("src/main/java/room/testapp/NewUtil.java")
+ .writeText("""
+ package room.testapp;
+ public class NewUtil {
+ }
+ """.trimIndent())
+
+ // Incremental build, compile task re-runs because of new source, but no schema is copied
+ // since Room processor didn't even run.
+ runGradleTasks(COMPILE_TASK).let { result ->
+ result.assertTaskOutcome(COMPILE_TASK, TaskOutcome.SUCCESS)
+ result.assertTaskOutcome(COPY_TASK, TaskOutcome.NO_SOURCE)
+ }
+
+ // Incremental build, everything is up to date.
+ runGradleTasks(COMPILE_TASK).let { result ->
+ result.assertTaskOutcome(COMPILE_TASK, TaskOutcome.UP_TO_DATE)
+ result.assertTaskOutcome(COPY_TASK, TaskOutcome.NO_SOURCE)
+ }
+
+ // Change the database version to 2
+ val dbFile = if (backend.isForKotlin) "MyDatabase.kt" else "MyDatabase.java"
+ searchAndReplace(
+ file = projectSetup.rootDir.resolve("src/main/java/room/testapp/$dbFile"),
+ search = "version = 1",
+ replace = "version = 2"
+ )
+
+ // Incremental build, due to the version change a new schema file is generated.
+ runGradleTasks(COMPILE_TASK).let { result ->
+ result.assertTaskOutcome(COMPILE_TASK, TaskOutcome.SUCCESS)
+ result.assertTaskOutcome(COPY_TASK, TaskOutcome.SUCCESS)
+ }
+
+ // Check schema file at version 1 is still present and unchanged.
+ projectSetup.rootDir.resolve("schemas/debug/room.testapp.MyDatabase/1.json").let {
+ assertThat(it.exists()).isTrue()
+ assertThat(schemaOneTimestamp).isEqualTo(it.lastModified())
+ }
+
+ // Check schema file at version 2 is created and copied.
+ projectSetup.rootDir.resolve("schemas/debug/room.testapp.MyDatabase/2.json").let {
+ assertThat(it.exists()).isTrue()
+ }
+ }
+
+ @Test
+ fun testFlavoredProject() {
+ setup("flavored-project")
+
+ File(projectSetup.rootDir, "build.gradle").appendText(
+ """
+ android {
+ flavorDimensions "mode"
+ productFlavors {
+ flavorOne {
+ dimension "mode"
+ }
+ flavorTwo {
+ dimension "mode"
+ }
+ }
+ }
+ """.trimIndent()
+ )
+
+ runGradleTasks(
+ CLEAN_TASK,
+ "compileFlavorOneDebugJavaWithJavac",
+ "compileFlavorTwoDebugJavaWithJavac"
+ ).let { result ->
+ result.assertTaskOutcome(":compileFlavorOneDebugJavaWithJavac", TaskOutcome.SUCCESS)
+ result.assertTaskOutcome(":compileFlavorTwoDebugJavaWithJavac", TaskOutcome.SUCCESS)
+ result.assertTaskOutcome(":copyRoomSchemasFlavorOneDebug", TaskOutcome.SUCCESS)
+ result.assertTaskOutcome(":copyRoomSchemasFlavorTwoDebug", TaskOutcome.SUCCESS)
+ }
+ // Check schema files are generated for both flavor, each in its own folder.
+ val flavorOneSchema = projectSetup.rootDir.resolve(
+ "schemas/flavorOneDebug/room.testapp.MyDatabase/1.json"
+ )
+ val flavorTwoSchema = projectSetup.rootDir.resolve(
+ "schemas/flavorTwoDebug/room.testapp.MyDatabase/1.json"
+ )
+ assertThat(flavorOneSchema.exists()).isTrue()
+ assertThat(flavorTwoSchema.exists()).isTrue()
+ // Check the schemas in both flavors are different
+ assertThat(flavorOneSchema.readText()).isNotEqualTo(flavorTwoSchema.readText())
+ }
+
+ @Test
+ fun testMoreBuildTypesProject() {
+ setup("simple-project")
+
+ File(projectSetup.rootDir, "build.gradle").appendText(
+ """
+ android {
+ buildTypes {
+ staging {
+ initWith debug
+ applicationIdSuffix ".debugStaging"
+ }
+ }
+ }
+ """.trimIndent()
+ )
+
+ runGradleTasks(CLEAN_TASK, "compileStagingJavaWithJavac",).let { result ->
+ result.assertTaskOutcome(":compileStagingJavaWithJavac", TaskOutcome.SUCCESS)
+ result.assertTaskOutcome(":copyRoomSchemasStaging", TaskOutcome.SUCCESS)
+ }
+ val schemeFile = projectSetup.rootDir.resolve(
+ "schemas/staging/room.testapp.MyDatabase/1.json"
+ )
+ assertThat(schemeFile.exists()).isTrue()
+ }
+
+ private fun runGradleTasks(
+ vararg args: String,
+ projectDir: File = projectSetup.rootDir
+ ): BuildResult {
+ return GradleRunner.create()
+ .withProjectDir(projectDir)
+ .withPluginClasspath()
+ // workaround for b/231154556
+ .withArguments("-Dorg.gradle.jvmargs=-Xmx1g -XX:MaxMetaspaceSize=512m", *args)
+ .build()
+ }
+
+ private fun BuildResult.assertTaskOutcome(taskPath: String, outcome: TaskOutcome) {
+ assertThat(this.task(taskPath)!!.outcome).isEqualTo(outcome)
+ }
+
+ private fun searchAndReplace(file: File, search: String, replace: String) {
+ file.writeText(file.readText().replace(search, replace))
+ }
+
+ enum class ProcessingBackend(
+ val isForKotlin: Boolean
+ ) {
+ JAVAC(false),
+ KAPT(true),
+ KSP(true)
+ }
+
+ companion object {
+ private const val CLEAN_TASK = ":clean"
+ private const val COMPILE_TASK = ":compileDebugJavaWithJavac"
+ private const val COPY_TASK = ":copyRoomSchemasDebug"
+ }
+}
\ No newline at end of file
diff --git a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt b/room/room-gradle-plugin/src/test/test-data/flavored-project/src/flavorOne/java/room/testapp/MyEntity.java
similarity index 72%
rename from fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
rename to room/room-gradle-plugin/src/test/test-data/flavored-project/src/flavorOne/java/room/testapp/MyEntity.java
index 9c66c18..8959321 100644
--- a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
+++ b/room/room-gradle-plugin/src/test/test-data/flavored-project/src/flavorOne/java/room/testapp/MyEntity.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,8 +14,13 @@
* limitations under the License.
*/
-package androidx.fragment.testapp
+package room.testapp;
-import androidx.fragment.app.Fragment
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
-class AnimationTestsFragment : Fragment(R.layout.animation_fragment)
+@Entity
+public class MyEntity {
+ @PrimaryKey
+ public long id;
+}
diff --git a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt b/room/room-gradle-plugin/src/test/test-data/flavored-project/src/flavorTwo/java/room/testapp/MyEntity.java
similarity index 69%
copy from fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
copy to room/room-gradle-plugin/src/test/test-data/flavored-project/src/flavorTwo/java/room/testapp/MyEntity.java
index 9c66c18..ea059e9 100644
--- a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
+++ b/room/room-gradle-plugin/src/test/test-data/flavored-project/src/flavorTwo/java/room/testapp/MyEntity.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,8 +14,15 @@
* limitations under the License.
*/
-package androidx.fragment.testapp
+package room.testapp;
-import androidx.fragment.app.Fragment
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
-class AnimationTestsFragment : Fragment(R.layout.animation_fragment)
+@Entity
+public class MyEntity {
+ @PrimaryKey
+ public long id;
+
+ public boolean flavorTwoColumn;
+}
diff --git a/room/room-gradle-plugin/src/test/test-data/flavored-project/src/main/AndroidManifest.xml b/room/room-gradle-plugin/src/test/test-data/flavored-project/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1e3e702
--- /dev/null
+++ b/room/room-gradle-plugin/src/test/test-data/flavored-project/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<manifest/>
\ No newline at end of file
diff --git a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt b/room/room-gradle-plugin/src/test/test-data/flavored-project/src/main/java/room/testapp/MyDao.java
similarity index 68%
copy from fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
copy to room/room-gradle-plugin/src/test/test-data/flavored-project/src/main/java/room/testapp/MyDao.java
index 9c66c18..727ffbc 100644
--- a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
+++ b/room/room-gradle-plugin/src/test/test-data/flavored-project/src/main/java/room/testapp/MyDao.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,8 +14,15 @@
* limitations under the License.
*/
-package androidx.fragment.testapp
+package room.testapp;
-import androidx.fragment.app.Fragment
+import androidx.room.Dao;
+import androidx.room.Query;
-class AnimationTestsFragment : Fragment(R.layout.animation_fragment)
+import java.util.List;
+
+@Dao
+public interface MyDao {
+ @Query("SELECT * FROM MyEntity")
+ List<MyEntity> getAll();
+}
diff --git a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt b/room/room-gradle-plugin/src/test/test-data/java/MyDatabase.java
similarity index 65%
copy from fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
copy to room/room-gradle-plugin/src/test/test-data/java/MyDatabase.java
index 9c66c18..29851d4 100644
--- a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
+++ b/room/room-gradle-plugin/src/test/test-data/java/MyDatabase.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,8 +14,12 @@
* limitations under the License.
*/
-package androidx.fragment.testapp
+package room.testapp;
-import androidx.fragment.app.Fragment
+import androidx.room.Database;
+import androidx.room.RoomDatabase;
-class AnimationTestsFragment : Fragment(R.layout.animation_fragment)
+@Database(entities = { MyEntity.class }, version = 1)
+public abstract class MyDatabase extends RoomDatabase {
+ public abstract MyDao getDao();
+}
diff --git a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt b/room/room-gradle-plugin/src/test/test-data/kotlin/MyDatabase.kt
similarity index 67%
copy from fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
copy to room/room-gradle-plugin/src/test/test-data/kotlin/MyDatabase.kt
index 9c66c18..8b03a9f 100644
--- a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
+++ b/room/room-gradle-plugin/src/test/test-data/kotlin/MyDatabase.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,8 +14,12 @@
* limitations under the License.
*/
-package androidx.fragment.testapp
+package room.testapp
-import androidx.fragment.app.Fragment
+import androidx.room.Database
+import androidx.room.RoomDatabase
-class AnimationTestsFragment : Fragment(R.layout.animation_fragment)
+@Database(entities = [MyEntity::class], version = 1)
+abstract class MyDatabase : RoomDatabase() {
+ abstract fun getDao(): MyDao
+}
\ No newline at end of file
diff --git a/room/room-gradle-plugin/src/test/test-data/simple-project/src/main/AndroidManifest.xml b/room/room-gradle-plugin/src/test/test-data/simple-project/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1e3e702
--- /dev/null
+++ b/room/room-gradle-plugin/src/test/test-data/simple-project/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<manifest/>
\ No newline at end of file
diff --git a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt b/room/room-gradle-plugin/src/test/test-data/simple-project/src/main/java/room/testapp/MyDao.java
similarity index 68%
copy from fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
copy to room/room-gradle-plugin/src/test/test-data/simple-project/src/main/java/room/testapp/MyDao.java
index 9c66c18..727ffbc 100644
--- a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
+++ b/room/room-gradle-plugin/src/test/test-data/simple-project/src/main/java/room/testapp/MyDao.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,8 +14,15 @@
* limitations under the License.
*/
-package androidx.fragment.testapp
+package room.testapp;
-import androidx.fragment.app.Fragment
+import androidx.room.Dao;
+import androidx.room.Query;
-class AnimationTestsFragment : Fragment(R.layout.animation_fragment)
+import java.util.List;
+
+@Dao
+public interface MyDao {
+ @Query("SELECT * FROM MyEntity")
+ List<MyEntity> getAll();
+}
diff --git a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt b/room/room-gradle-plugin/src/test/test-data/simple-project/src/main/java/room/testapp/MyEntity.java
similarity index 70%
copy from fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
copy to room/room-gradle-plugin/src/test/test-data/simple-project/src/main/java/room/testapp/MyEntity.java
index 9c66c18..3e4c1c0 100644
--- a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/AnimationTestsFragment.kt
+++ b/room/room-gradle-plugin/src/test/test-data/simple-project/src/main/java/room/testapp/MyEntity.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,8 +14,15 @@
* limitations under the License.
*/
-package androidx.fragment.testapp
+package room.testapp;
-import androidx.fragment.app.Fragment
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
-class AnimationTestsFragment : Fragment(R.layout.animation_fragment)
+@Entity
+public class MyEntity {
+ @PrimaryKey
+ public long id;
+
+ // Insert-change
+}
diff --git a/room/room-guava/build.gradle b/room/room-guava/build.gradle
index ef2b7b79..99c5ad7 100644
--- a/room/room-guava/build.gradle
+++ b/room/room-guava/build.gradle
@@ -26,7 +26,7 @@
api(project(":room:room-runtime")) {
exclude group: "com.google.guava", module: "listenablefuture"
}
- implementation("androidx.arch.core:core-runtime:2.0.1")
+ implementation("androidx.arch.core:core-runtime:2.2.0")
api("androidx.annotation:annotation:1.0.0")
implementation("androidx.concurrent:concurrent-futures:1.0.0")
androidTestImplementation(libs.testRunner)
diff --git a/room/room-paging-guava/build.gradle b/room/room-paging-guava/build.gradle
index 723c5da..e40d8bc 100644
--- a/room/room-paging-guava/build.gradle
+++ b/room/room-paging-guava/build.gradle
@@ -38,7 +38,7 @@
androidTestImplementation(libs.kotlinCoroutinesTest)
androidTestImplementation(libs.kotlinCoroutinesGuava)
androidTestImplementation(libs.guavaAndroid)
- androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+ androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
androidTestImplementation(project(":internal-testutils-common"))
kspAndroidTest(
project(path: ":room:room-compiler", configuration: "shadowAndImplementation")
diff --git a/room/room-paging-rxjava2/build.gradle b/room/room-paging-rxjava2/build.gradle
index d4f2bd4..d9b9d5d 100644
--- a/room/room-paging-rxjava2/build.gradle
+++ b/room/room-paging-rxjava2/build.gradle
@@ -37,7 +37,7 @@
androidTestImplementation(libs.kotlinTestJunit) //
androidTestImplementation(libs.kotlinCoroutinesTest)
androidTestImplementation(libs.kotlinCoroutinesRx2)
- androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+ androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
androidTestImplementation(project(":internal-testutils-common"))
kspAndroidTest(
project(path: ":room:room-compiler", configuration: "shadowAndImplementation")
diff --git a/room/room-paging-rxjava3/build.gradle b/room/room-paging-rxjava3/build.gradle
index 11a3bb65..a4ef997 100644
--- a/room/room-paging-rxjava3/build.gradle
+++ b/room/room-paging-rxjava3/build.gradle
@@ -37,7 +37,7 @@
androidTestImplementation(libs.kotlinCoroutinesRx3)
androidTestImplementation(libs.kotlinTestJunit) //
androidTestImplementation(libs.kotlinCoroutinesTest)
- androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+ androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
androidTestImplementation(project(":internal-testutils-common"))
kspAndroidTest(
project(path: ":room:room-compiler", configuration: "shadowAndImplementation")
diff --git a/room/room-paging/build.gradle b/room/room-paging/build.gradle
index adbfb9b..31a7c7f 100644
--- a/room/room-paging/build.gradle
+++ b/room/room-paging/build.gradle
@@ -50,7 +50,7 @@
project(path: ":room:room-compiler", configuration: "shadowAndImplementation")
)
androidTestImplementation(libs.truth)
- androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+ androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
androidTestImplementation(project(":internal-testutils-common"))
androidTestImplementation(projectOrArtifact(":paging:paging-testing"))
}
diff --git a/room/room-runtime/build.gradle b/room/room-runtime/build.gradle
index aff3eb8..5859784 100644
--- a/room/room-runtime/build.gradle
+++ b/room/room-runtime/build.gradle
@@ -41,7 +41,7 @@
api(project(":room:room-common"))
api(project(":sqlite:sqlite-framework"))
api(project(":sqlite:sqlite"))
- implementation("androidx.arch.core:core-runtime:2.0.1")
+ implementation("androidx.arch.core:core-runtime:2.2.0")
compileOnly("androidx.collection:collection:1.2.0")
compileOnly("androidx.paging:paging-common:2.0.0")
compileOnly("androidx.lifecycle:lifecycle-livedata-core:2.0.0")
@@ -49,7 +49,7 @@
compileOnly libs.kotlinStdlib // Due to :annotation-experimental
lintChecks(project(":room:room-runtime-lint"))
- testImplementation("androidx.arch.core:core-testing:2.0.1")
+ testImplementation("androidx.arch.core:core-testing:2.2.0")
testImplementation(libs.junit)
testImplementation(libs.mockitoCore4)
testImplementation(libs.mockitoKotlin4)
@@ -68,7 +68,7 @@
androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it"s own MockMaker
androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it"s own MockMaker
androidTestImplementation(project(":internal-testutils-truth")) // for assertThrows
- androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+ androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
}
diff --git a/room/room-runtime/proguard-rules.pro b/room/room-runtime/proguard-rules.pro
index 57eff04..60b1670 100644
--- a/room/room-runtime/proguard-rules.pro
+++ b/room/room-runtime/proguard-rules.pro
@@ -1,2 +1,3 @@
-keep class * extends androidx.room.RoomDatabase
-dontwarn androidx.room.paging.**
+-dontwarn androidx.lifecycle.LiveData
diff --git a/room/room-rxjava2/build.gradle b/room/room-rxjava2/build.gradle
index a9a4adb..6cc2e1e 100644
--- a/room/room-rxjava2/build.gradle
+++ b/room/room-rxjava2/build.gradle
@@ -27,14 +27,14 @@
api(project(":room:room-runtime"))
api(libs.rxjava2)
- implementation("androidx.arch.core:core-runtime:2.0.1")
+ implementation("androidx.arch.core:core-runtime:2.2.0")
implementation(libs.kotlinStdlib)
testImplementation(libs.truth)
testImplementation(libs.kotlinTest)
testImplementation(libs.mockitoCore4)
testImplementation(libs.mockitoKotlin4)
- testImplementation("androidx.arch.core:core-testing:2.0.1")
+ testImplementation("androidx.arch.core:core-testing:2.2.0")
testImplementation("androidx.lifecycle:lifecycle-livedata:2.0.0") // for mocking invalidation tracker
}
diff --git a/room/room-rxjava3/build.gradle b/room/room-rxjava3/build.gradle
index 8369f03..fee4abe 100644
--- a/room/room-rxjava3/build.gradle
+++ b/room/room-rxjava3/build.gradle
@@ -28,14 +28,14 @@
api(project(":room:room-runtime"))
api(libs.rxjava3)
- implementation("androidx.arch.core:core-runtime:2.0.1")
+ implementation("androidx.arch.core:core-runtime:2.2.0")
implementation(libs.kotlinStdlib)
testImplementation(libs.truth)
testImplementation(libs.kotlinTest)
testImplementation(libs.mockitoCore4)
testImplementation(libs.mockitoKotlin4)
- testImplementation("androidx.arch.core:core-testing:2.0.1")
+ testImplementation("androidx.arch.core:core-testing:2.2.0")
testImplementation("androidx.lifecycle:lifecycle-livedata:2.0.0") // for mocking invalidation tracker
}
diff --git a/room/room-testing/build.gradle b/room/room-testing/build.gradle
index 3fc9bce..1471e49 100644
--- a/room/room-testing/build.gradle
+++ b/room/room-testing/build.gradle
@@ -37,7 +37,7 @@
api(project(":sqlite:sqlite-framework"))
api(project(":room:room-migration"))
api(libs.junit)
- implementation("androidx.arch.core:core-runtime:2.0.1")
+ implementation("androidx.arch.core:core-runtime:2.2.0")
androidTestImplementation(libs.truth)
androidTestImplementation(libs.kotlinStdlib)
androidTestImplementation(libs.testExtJunit)
diff --git a/room/scripts/update_ksp.sh b/room/scripts/update_ksp.sh
index 04dceed..6b8d3fa 100755
--- a/room/scripts/update_ksp.sh
+++ b/room/scripts/update_ksp.sh
@@ -41,8 +41,8 @@
# other projects depend on ksp prebuilts so we don't delete them anymore.
# download
-development/importMaven/import_maven_artifacts.py -n com.google.devtools.ksp:symbol-processing-gradle-plugin:$KSP_VERSION
-development/importMaven/import_maven_artifacts.py -n com.google.devtools.ksp:symbol-processing:$KSP_VERSION
+development/importMaven/importMaven.sh com.google.devtools.ksp:symbol-processing-gradle-plugin:$KSP_VERSION
+development/importMaven/importMaven.sh com.google.devtools.ksp:symbol-processing:$KSP_VERSION
# update build version
sed -i '' "s/ksp = \".*\"/ksp = \"$KSP_VERSION\"/" gradle/libs.versions.toml
diff --git a/savedstate/savedstate/build.gradle b/savedstate/savedstate/build.gradle
index a102643..f9ca694 100644
--- a/savedstate/savedstate/build.gradle
+++ b/savedstate/savedstate/build.gradle
@@ -15,7 +15,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- implementation("androidx.arch.core:core-common:2.1.0")
+ implementation("androidx.arch.core:core-common:2.2.0")
implementation("androidx.lifecycle:lifecycle-common:2.6.1")
api(libs.kotlinStdlib)
diff --git a/settings.gradle b/settings.gradle
index 6dc200a..9abd3a2 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,6 +1,6 @@
-import groovy.transform.Field
import androidx.build.gradle.gcpbuildcache.GcpBuildCache
import androidx.build.gradle.gcpbuildcache.GcpBuildCacheServiceFactory
+import groovy.transform.Field
import java.util.regex.Matcher
import java.util.regex.Pattern
@@ -195,7 +195,8 @@
WEAR,
GLANCE,
TOOLS,
- KMP,
+ KMP, // All projects built as Kotlin Multi Platform (compose, datastore, collections, etc).
+ INFRAROGUE, // Projects built by playground team, mostly non-compose kmp.
CAMERA,
NATIVE,
WINDOW,
@@ -255,6 +256,9 @@
case "KMP":
filter.add(BuildType.KMP)
break
+ case "INFRAROGUE":
+ filter.add(BuildType.INFRAROGUE)
+ break
case "CAMERA":
filter.add(BuildType.CAMERA)
break
@@ -449,10 +453,10 @@
includeProject(":autofill:autofill", [BuildType.MAIN])
includeProject(":benchmark:benchmark-benchmark", "benchmark/benchmark", [BuildType.MAIN, BuildType.COMPOSE])
includeProject(":benchmark:benchmark-common")
-includeProject(":benchmark:benchmark-darwin", [BuildType.KMP])
-includeProject(":benchmark:benchmark-darwin-core", [BuildType.KMP])
-includeProject(":benchmark:benchmark-darwin-samples", [BuildType.KMP])
-includeProject(":benchmark:benchmark-darwin-gradle-plugin", [BuildType.KMP])
+includeProject(":benchmark:benchmark-darwin", [BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":benchmark:benchmark-darwin-core", [BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":benchmark:benchmark-darwin-samples", [BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":benchmark:benchmark-darwin-gradle-plugin", [BuildType.INFRAROGUE, BuildType.KMP])
includeProject(":benchmark:benchmark-gradle-plugin", "benchmark/gradle-plugin", [BuildType.MAIN])
includeProject(":benchmark:benchmark-baseline-profile-gradle-plugin", "benchmark/baseline-profile-gradle-plugin",[BuildType.MAIN])
includeProject(":benchmark:benchmark-junit4")
@@ -521,11 +525,11 @@
includeProject(":car:app:app-samples:showcase-mobile", "car/app/app-samples/showcase/mobile", [BuildType.MAIN])
includeProject(":car:app:app-testing", [BuildType.MAIN])
includeProject(":cardview:cardview", [BuildType.MAIN])
-includeProject(":collection:collection", [BuildType.MAIN, BuildType.KMP])
-includeProject(":collection:collection-benchmark", [BuildType.MAIN, BuildType.KMP])
-includeProject(":collection:collection-benchmark-kmp", [BuildType.MAIN, BuildType.KMP])
-includeProject(":collection:collection-ktx", [BuildType.MAIN, BuildType.KMP])
-includeProject(":collection:integration-tests:testapp", [BuildType.MAIN, BuildType.KMP])
+includeProject(":collection:collection", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":collection:collection-benchmark", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":collection:collection-benchmark-kmp", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":collection:collection-ktx", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":collection:integration-tests:testapp", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
includeProject(":compose:animation", [BuildType.COMPOSE])
includeProject(":compose:animation:animation", [BuildType.COMPOSE])
includeProject(":compose:animation:animation-lint", [BuildType.COMPOSE])
@@ -549,9 +553,9 @@
includeProject(":compose:compiler:compiler-daemon:integration-tests", [BuildType.COMPOSE])
if (isMultiplatformEnabled()) {
- includeProject(":compose:desktop", [BuildType.COMPOSE])
- includeProject(":compose:desktop:desktop", [BuildType.COMPOSE])
- includeProject(":compose:desktop:desktop:desktop-samples", "compose/desktop/desktop/samples", [BuildType.COMPOSE])
+ includeProject(":compose:desktop", [BuildType.COMPOSE, BuildType.KMP])
+ includeProject(":compose:desktop:desktop", [BuildType.COMPOSE, BuildType.KMP])
+ includeProject(":compose:desktop:desktop:desktop-samples", "compose/desktop/desktop/samples", [BuildType.COMPOSE, BuildType.KMP])
}
includeProject(":compose:foundation", [BuildType.COMPOSE])
includeProject(":compose:foundation:foundation", [BuildType.COMPOSE])
@@ -601,7 +605,7 @@
includeProject(":compose:material:material:material-samples", "compose/material/material/samples", [BuildType.COMPOSE])
includeProject(":compose:material3:material3:material3-samples", "compose/material3/material3/samples", [BuildType.COMPOSE])
includeProject(":compose:runtime", [BuildType.COMPOSE])
-includeProject(":compose:runtime:runtime", [BuildType.COMPOSE])
+includeProject(":compose:runtime:runtime", [BuildType.COMPOSE, BuildType.KMP])
includeProject(":compose:runtime:runtime-lint", [BuildType.COMPOSE])
includeProject(":compose:runtime:runtime-livedata", [BuildType.COMPOSE])
includeProject(":compose:runtime:runtime-livedata:runtime-livedata-samples", "compose/runtime/runtime-livedata/samples", [BuildType.COMPOSE])
@@ -687,20 +691,20 @@
includeProject(":cursoradapter:cursoradapter", [BuildType.MAIN])
includeProject(":customview:customview", [BuildType.MAIN])
includeProject(":customview:customview-poolingcontainer", [BuildType.MAIN, BuildType.COMPOSE])
-includeProject(":datastore:datastore", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-benchmark", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-core", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-core-okio", [BuildType.MAIN, BuildType.KMP])
+includeProject(":datastore:datastore", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-benchmark", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-core", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-core-okio", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
includeProject(":datastore:datastore-compose-samples", [BuildType.COMPOSE])
-includeProject(":datastore:datastore-preferences", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-preferences-core", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-preferences-proto", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-preferences-rxjava2", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-preferences-rxjava3", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-proto", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-rxjava2", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-rxjava3", [BuildType.MAIN, BuildType.KMP])
-includeProject(":datastore:datastore-sampleapp", [BuildType.MAIN, BuildType.KMP])
+includeProject(":datastore:datastore-preferences", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-preferences-core", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-preferences-proto", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-preferences-rxjava2", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-preferences-rxjava3", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-proto", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-rxjava2", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-rxjava3", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-sampleapp", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
includeProject(":documentfile:documentfile", [BuildType.MAIN])
includeProject(":draganddrop:draganddrop", [BuildType.MAIN])
includeProject(":draganddrop:integration-tests:sampleapp", [BuildType.MAIN])
@@ -918,6 +922,7 @@
includeProject(":room:room-compiler-processing", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN])
includeProject(":room:room-compiler-processing-testing", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN])
includeProject(":room:room-guava", [BuildType.MAIN])
+includeProject(":room:room-gradle-plugin", [BuildType.MAIN])
includeProject(":room:room-ktx", [BuildType.MAIN, BuildType.COMPOSE])
includeProject(":room:room-migration", [BuildType.MAIN, BuildType.COMPOSE])
includeProject(":room:room-paging", [BuildType.MAIN, BuildType.COMPOSE])
@@ -1118,14 +1123,14 @@
/////////////////////////////
includeProject(":internal-testutils-common", "testutils/testutils-common", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN])
-includeProject(":internal-testutils-datastore", "testutils/testutils-datastore", [BuildType.MAIN, BuildType.KMP])
-includeProject(":internal-testutils-runtime", "testutils/testutils-runtime", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE, BuildType.MEDIA])
+includeProject(":internal-testutils-datastore", "testutils/testutils-datastore", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":internal-testutils-runtime", "testutils/testutils-runtime", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE, BuildType.MEDIA, BuildType.WEAR])
includeProject(":internal-testutils-appcompat", "testutils/testutils-appcompat", [BuildType.MAIN])
includeProject(":internal-testutils-espresso", "testutils/testutils-espresso", [BuildType.MAIN, BuildType.COMPOSE])
includeProject(":internal-testutils-fonts", "testutils/testutils-fonts", [BuildType.MAIN, BuildType.GLANCE, BuildType.MEDIA, BuildType.FLAN, BuildType.COMPOSE])
includeProject(":internal-testutils-truth", "testutils/testutils-truth")
includeProject(":internal-testutils-ktx", "testutils/testutils-ktx")
-includeProject(":internal-testutils-kmp", "testutils/testutils-kmp", [BuildType.MAIN, BuildType.KMP, BuildType.COMPOSE])
+includeProject(":internal-testutils-kmp", "testutils/testutils-kmp", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP, BuildType.COMPOSE])
includeProject(":internal-testutils-macrobenchmark", "testutils/testutils-macrobenchmark", [BuildType.MAIN, BuildType.COMPOSE])
includeProject(":internal-testutils-navigation", "testutils/testutils-navigation", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN])
includeProject(":internal-testutils-paging", "testutils/testutils-paging", [BuildType.MAIN, BuildType.COMPOSE])
@@ -1179,7 +1184,7 @@
includeProject(":docs-public")
}
-includeProject(":docs-kmp", [BuildType.KMP])
+includeProject(":docs-kmp", [BuildType.KMP, BuildType.INFRAROGUE])
// placeholder test project that has a test for each size to ensure that at least one test is run
// for each size and test runner is happy when there is nothing to test.
includeProject(":placeholder-tests")
diff --git a/slice/slice-view/src/main/res/values-da/strings.xml b/slice/slice-view/src/main/res/values-da/strings.xml
index 1491d22..379bfdb 100644
--- a/slice/slice-view/src/main/res/values-da/strings.xml
+++ b/slice/slice-view/src/main/res/values-da/strings.xml
@@ -30,8 +30,8 @@
<item quantity="other">For <xliff:g id="ID_2">%d</xliff:g> år siden</item>
</plurals>
<plurals name="abc_slice_duration_days" formatted="false" msgid="8356547162075064530">
- <item quantity="one">For <xliff:g id="ID_2">%d</xliff:g> dag siden</item>
- <item quantity="other">For <xliff:g id="ID_2">%d</xliff:g> dage siden</item>
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> dag siden</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> dage siden</item>
</plurals>
<string name="abc_slice_error" msgid="1794214973158263497">"Der kunne ikke oprettes forbindelse"</string>
</resources>
diff --git a/startup/integration-tests/test-app/build.gradle b/startup/integration-tests/test-app/build.gradle
index 7dbe4b8..93f6d4d 100644
--- a/startup/integration-tests/test-app/build.gradle
+++ b/startup/integration-tests/test-app/build.gradle
@@ -35,6 +35,6 @@
implementation(project(":startup:integration-tests:first-library"))
implementation(project(":startup:integration-tests:second-library"))
implementation(libs.constraintLayout)
- implementation("androidx.arch.core:core-runtime:2.1.0")
+ implementation("androidx.arch.core:core-runtime:2.2.0")
implementation("androidx.appcompat:appcompat:1.2.0")
}
diff --git a/testutils/testutils-fonts/build.gradle b/testutils/testutils-fonts/build.gradle
index dffd039..911cf9b 100644
--- a/testutils/testutils-fonts/build.gradle
+++ b/testutils/testutils-fonts/build.gradle
@@ -14,12 +14,20 @@
* limitations under the License.
*/
+
+import androidx.build.KmpPlatformsKt
import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
id("com.android.library")
- id("kotlin-android")
+}
+
+def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
+
+androidXMultiplatform {
+ android()
+ if (desktopEnabled) desktop()
}
dependencies {
diff --git a/testutils/testutils-gradle-plugin/src/main/java/androidx/testutils/gradle/ProjectSetupRule.kt b/testutils/testutils-gradle-plugin/src/main/java/androidx/testutils/gradle/ProjectSetupRule.kt
index 7bd12a1c..0724f1f 100644
--- a/testutils/testutils-gradle-plugin/src/main/java/androidx/testutils/gradle/ProjectSetupRule.kt
+++ b/testutils/testutils-gradle-plugin/src/main/java/androidx/testutils/gradle/ProjectSetupRule.kt
@@ -16,12 +16,15 @@
package androidx.testutils.gradle
+import java.io.File
+import java.util.Properties
+import javax.xml.parsers.DocumentBuilderFactory
+import javax.xml.xpath.XPathConstants
+import javax.xml.xpath.XPathFactory
import org.junit.rules.ExternalResource
import org.junit.rules.TemporaryFolder
import org.junit.runner.Description
import org.junit.runners.model.Statement
-import java.io.File
-import java.util.Properties
/**
* Test rule that helps to setup android project in tests that run gradle.
@@ -130,6 +133,42 @@
}
}
+ /**
+ * Gets the latest version of a published library.
+ *
+ * Note that the library must have been locally published to locate its latest version, this
+ * can be done in test by adding :publish as a test dependency, for example:
+ * ```
+ * tasks.findByPath("test")
+ * .dependsOn(tasks.findByPath(":room:room-compiler:publish")
+ * ```
+ *
+ * @param path - The library m2 path e.g. "androidx/room/room-compiler"
+ */
+ fun getLibraryLatestVersionInLocalRepo(path: String): String {
+ val metadataFile = File(props.tipOfTreeMavenRepoPath)
+ .resolve(path)
+ .resolve("maven-metadata.xml")
+ check(metadataFile.exists()) {
+ "Cannot find room metadata file in ${metadataFile.absolutePath}"
+ }
+ check(metadataFile.isFile) {
+ "Metadata file should be a file but it is not."
+ }
+ val xmlDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
+ .parse(metadataFile)
+ val latestVersionNode = XPathFactory.newInstance().newXPath()
+ .compile("/metadata/versioning/latest").evaluate(
+ xmlDoc, XPathConstants.STRING
+ )
+ check(latestVersionNode is String) {
+ """Unexpected node for latest version:
+ $latestVersionNode / ${latestVersionNode::class.java}
+ """.trimIndent()
+ }
+ return latestVersionNode
+ }
+
private fun copyLocalProperties() {
var foundSdk = false
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt
index 02496f3..fc177b9 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt
@@ -59,12 +59,17 @@
const val HYPHENATION_FREQUENCY_NONE = Layout.HYPHENATION_FREQUENCY_NONE
const val HYPHENATION_FREQUENCY_NORMAL = Layout.HYPHENATION_FREQUENCY_NORMAL
const val HYPHENATION_FREQUENCY_NORMAL_FAST = Layout.HYPHENATION_FREQUENCY_NORMAL_FAST
+ const val HYPHENATION_FREQUENCY_FULL = Layout.HYPHENATION_FREQUENCY_FULL
+ const val HYPHENATION_FREQUENCY_FULL_FAST = Layout.HYPHENATION_FREQUENCY_FULL_FAST
@Retention(AnnotationRetention.SOURCE)
@IntDef(
+ HYPHENATION_FREQUENCY_NONE,
HYPHENATION_FREQUENCY_NORMAL,
HYPHENATION_FREQUENCY_NORMAL_FAST,
- HYPHENATION_FREQUENCY_NONE
+ HYPHENATION_FREQUENCY_FULL,
+ HYPHENATION_FREQUENCY_FULL_FAST
+
)
internal annotation class HyphenationFrequency
diff --git a/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java b/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java
index b9f58ca..1e55554 100644
--- a/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java
+++ b/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java
@@ -16,6 +16,8 @@
package androidx.tracing;
+import static androidx.tracing.Trace.MAX_TRACE_LABEL_LENGTH;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -77,6 +79,21 @@
}
@Test
+ public void beginAndEndTraceSectionLongLabel() throws IOException {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < 20; i++) {
+ builder.append("longLabel");
+ }
+ startTrace();
+ Trace.beginSection(builder.toString());
+ Trace.endSection();
+ dumpTrace();
+ assertTraceContains(
+ "tracing_mark_write:\\ B\\|.*\\|" + builder.substring(0, MAX_TRACE_LABEL_LENGTH));
+ assertTraceContains("tracing_mark_write:\\ E");
+ }
+
+ @Test
@SdkSuppress(minSdkVersion = 29) // SELinux
public void beginAndEndSectionAsync() throws IOException {
startTrace();
@@ -160,7 +177,6 @@
private void assertTraceContains(@NonNull String contentRegex) {
String traceString = new String(mByteArrayOutputStream.toByteArray(), UTF_8);
-
Pattern pattern = Pattern.compile(contentRegex);
Matcher matcher = pattern.matcher(traceString);
diff --git a/tracing/tracing/src/main/java/androidx/tracing/Trace.java b/tracing/tracing/src/main/java/androidx/tracing/Trace.java
index 8d224f9..9a7dcba 100644
--- a/tracing/tracing/src/main/java/androidx/tracing/Trace.java
+++ b/tracing/tracing/src/main/java/androidx/tracing/Trace.java
@@ -54,6 +54,7 @@
*/
public final class Trace {
static final String TAG = "Trace";
+ static final int MAX_TRACE_LABEL_LENGTH = 127;
private static long sTraceTagApp;
private static Method sIsTagEnabledMethod;
@@ -126,7 +127,7 @@
*/
public static void beginSection(@NonNull String label) {
if (Build.VERSION.SDK_INT >= 18) {
- TraceApi18Impl.beginSection(label);
+ TraceApi18Impl.beginSection(truncatedTraceSectionLabel(label));
}
}
@@ -175,9 +176,9 @@
*/
public static void beginAsyncSection(@NonNull String methodName, int cookie) {
if (Build.VERSION.SDK_INT >= 29) {
- TraceApi29Impl.beginAsyncSection(methodName, cookie);
+ TraceApi29Impl.beginAsyncSection(truncatedTraceSectionLabel(methodName), cookie);
} else {
- beginAsyncSectionFallback(methodName, cookie);
+ beginAsyncSectionFallback(truncatedTraceSectionLabel(methodName), cookie);
}
}
@@ -194,9 +195,9 @@
*/
public static void endAsyncSection(@NonNull String methodName, int cookie) {
if (Build.VERSION.SDK_INT >= 29) {
- TraceApi29Impl.endAsyncSection(methodName, cookie);
+ TraceApi29Impl.endAsyncSection(truncatedTraceSectionLabel(methodName), cookie);
} else {
- endAsyncSectionFallback(methodName, cookie);
+ endAsyncSectionFallback(truncatedTraceSectionLabel(methodName), cookie);
}
}
@@ -208,9 +209,9 @@
*/
public static void setCounter(@NonNull String counterName, int counterValue) {
if (Build.VERSION.SDK_INT >= 29) {
- TraceApi29Impl.setCounter(counterName, counterValue);
+ TraceApi29Impl.setCounter(truncatedTraceSectionLabel(counterName), counterValue);
} else {
- setCounterFallback(counterName, counterValue);
+ setCounterFallback(truncatedTraceSectionLabel(counterName), counterValue);
}
}
@@ -300,6 +301,14 @@
Log.v(TAG, "Unable to call " + methodName + " via reflection", exception);
}
+ @NonNull
+ private static String truncatedTraceSectionLabel(@NonNull String labelName) {
+ if (labelName.length() <= MAX_TRACE_LABEL_LENGTH) {
+ return labelName;
+ }
+ return labelName.substring(0, MAX_TRACE_LABEL_LENGTH);
+ }
+
private Trace() {
}
}
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt
index 6f6d535..855ac02 100644
--- a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt
@@ -16,6 +16,13 @@
package androidx.tv.integration.playground
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.slideInVertically
+import androidx.compose.animation.slideOutHorizontally
+import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.focusable
@@ -88,7 +95,7 @@
}
}
-@OptIn(ExperimentalTvMaterial3Api::class)
+@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
@Composable
internal fun FeaturedCarousel(modifier: Modifier = Modifier) {
val backgrounds = listOf(
@@ -117,33 +124,31 @@
.align(Alignment.BottomEnd)
.padding(16.dp),
)
- }
+ },
+ contentTransformStartToEnd =
+ fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000))),
+ contentTransformEndToStart =
+ fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000)))
) { itemIndex ->
- CarouselItem(
- modifier = Modifier.semantics {
- contentDescription = "Featured Content"
- },
- background = {
- Box(
- modifier = Modifier
- .background(backgrounds[itemIndex])
- .fillMaxSize()
- )
- },
+ Box(
+ modifier = Modifier
+ .background(backgrounds[itemIndex])
+ .fillMaxSize()
+ .semantics { contentDescription = "Featured Content" }
) {
- Box(
+ Column(
modifier = Modifier
- .fillMaxSize()
- .padding(20.dp),
- contentAlignment = Alignment.BottomStart
+ .padding(start = 50.dp, top = 100.dp)
+ .animateEnterExit(
+ enter = slideInVertically(animationSpec = tween(1000)),
+ exit = slideOutHorizontally(animationSpec = tween(1000))
+ )
) {
- Column {
- Text(text = "This is sample text content.", color = Color.Yellow)
- Text(text = "Sample description.", color = Color.Yellow)
- Row {
- OverlayButton(text = "Play")
- OverlayButton(text = "Add to Watchlist")
- }
+ Text(text = "This is sample text content.", color = Color.Yellow)
+ Text(text = "Sample description of slide ${itemIndex + 1}.", color = Color.Yellow)
+ Row {
+ OverlayButton(text = "Play")
+ OverlayButton(text = "Add to Watchlist")
}
}
}
diff --git a/tv/integration-tests/presentation/README.md b/tv/integration-tests/presentation/README.md
index eb86b24..9c46867 100644
--- a/tv/integration-tests/presentation/README.md
+++ b/tv/integration-tests/presentation/README.md
@@ -3,12 +3,11 @@
## Setup
* Uncomment the `coil` and `gson` libraries dependency additions from the `build.gradle` file.
+* Uncomment the function content and imports from
+ `presentation/src/main/java/androidx/tv/integration/presentation/ExternalLibs.kt` file
* Create the `data.json` file in `presentation/src/main/assets` directory and add the content from
this link: go/compose-tv-presentation-app-data
> If you are not a Googler and want to use this app for
> testing, you will have to create the `data.json` file by following the schema mentioned in the
`Data.kt` file
-
-* Uncomment the function content and imports from
- `presentation/src/main/java/androidx/tv/integration/presentation/ExternalLibs.kt` file
diff --git a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/FeaturedCarousel.kt b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/FeaturedCarousel.kt
index 33e5b16..94df400 100644
--- a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/FeaturedCarousel.kt
+++ b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/FeaturedCarousel.kt
@@ -16,6 +16,15 @@
package androidx.tv.integration.presentation
+import androidx.compose.animation.AnimatedContentScope
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.slideInHorizontally
+import androidx.compose.animation.slideOutHorizontally
+import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -32,7 +41,6 @@
import androidx.compose.ui.unit.sp
import androidx.tv.material3.Carousel
import androidx.tv.material3.CarouselDefaults
-import androidx.tv.material3.CarouselScope
import androidx.tv.material3.CarouselState
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
@@ -60,7 +68,11 @@
.align(Alignment.BottomEnd)
.padding(end = 58.dp, bottom = 16.dp),
)
- }
+ },
+ contentTransformEndToStart =
+ fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000))),
+ contentTransformStartToEnd =
+ fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000)))
) { itemIndex ->
val movie = movies[itemIndex]
@@ -80,22 +92,23 @@
}
}
-@OptIn(ExperimentalTvMaterial3Api::class)
+@OptIn(ExperimentalAnimationApi::class)
@Composable
-private fun CarouselScope.CarouselSlide(
+private fun AnimatedContentScope.CarouselSlide(
title: String,
description: String,
background: @Composable () -> Unit,
actions: @Composable () -> Unit
) {
- CarouselItem(
- background = {
- background()
- },
- modifier = Modifier
- ) {
+ Box {
+ background()
Column(
- modifier = Modifier.padding(start = 58.dp, top = 150.dp)
+ modifier = Modifier
+ .padding(start = 58.dp, top = 150.dp)
+ .animateEnterExit(
+ enter = slideInHorizontally(animationSpec = tween(1000)) { it / 2 },
+ exit = slideOutHorizontally(animationSpec = tween(1000))
+ )
) {
Text(
text = title,
diff --git a/tv/samples/src/main/java/androidx/tv/samples/CarouselSamples.kt b/tv/samples/src/main/java/androidx/tv/samples/CarouselSamples.kt
index aba7109..b356829 100644
--- a/tv/samples/src/main/java/androidx/tv/samples/CarouselSamples.kt
+++ b/tv/samples/src/main/java/androidx/tv/samples/CarouselSamples.kt
@@ -17,6 +17,13 @@
package androidx.tv.samples
import androidx.annotation.Sampled
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.slideInHorizontally
+import androidx.compose.animation.slideOutHorizontally
+import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
@@ -44,7 +51,7 @@
import androidx.tv.material3.CarouselState
import androidx.tv.material3.ExperimentalTvMaterial3Api
-@OptIn(ExperimentalTvMaterial3Api::class)
+@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
@Sampled
@Composable
fun SimpleCarousel() {
@@ -59,16 +66,16 @@
modifier = Modifier
.height(300.dp)
.fillMaxWidth(),
+ contentTransformEndToStart =
+ fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000))),
+ contentTransformStartToEnd =
+ fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000)))
) { itemIndex ->
- CarouselItem(
- background = {
- Box(
- modifier = Modifier
- .background(backgrounds[itemIndex])
- .border(2.dp, Color.White.copy(alpha = 0.5f))
- .fillMaxSize()
- )
- }
+ Box(
+ modifier = Modifier
+ .background(backgrounds[itemIndex])
+ .border(2.dp, Color.White.copy(alpha = 0.5f))
+ .fillMaxSize()
) {
var isFocused by remember { mutableStateOf(false) }
@@ -82,6 +89,13 @@
color = if (isFocused) Color.Red else Color.Transparent,
shape = RoundedCornerShape(50)
)
+ // Duration of animation here should be less than or equal to carousel's
+ // contentTransform duration to ensure the item below does not disappear
+ // abruptly.
+ .animateEnterExit(
+ enter = slideInHorizontally(animationSpec = tween(1000)) { it / 2 },
+ exit = slideOutHorizontally(animationSpec = tween(1000))
+ )
.padding(vertical = 2.dp, horizontal = 5.dp)
) {
Text(text = "Play")
@@ -90,7 +104,7 @@
}
}
-@OptIn(ExperimentalTvMaterial3Api::class)
+@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
@Sampled
@Composable
fun CarouselIndicatorWithRectangleShape() {
@@ -127,24 +141,30 @@
)
}
)
- }
+ },
+ contentTransformEndToStart =
+ fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000))),
+ contentTransformStartToEnd =
+ fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000)))
) { itemIndex ->
- CarouselItem(
- background = {
- Box(
- modifier = Modifier
- .background(backgrounds[itemIndex])
- .border(2.dp, Color.White.copy(alpha = 0.5f))
- .fillMaxSize()
- )
- }
+ Box(
+ modifier = Modifier
+ .background(backgrounds[itemIndex])
+ .border(2.dp, Color.White.copy(alpha = 0.5f))
+ .fillMaxSize()
) {
var isFocused by remember { mutableStateOf(false) }
-
Button(
onClick = { },
modifier = Modifier
.onFocusChanged { isFocused = it.isFocused }
+ // Duration of animation here should be less than or equal to carousel's
+ // contentTransform duration to ensure the item below does not disappear
+ // abruptly.
+ .animateEnterExit(
+ enter = slideInHorizontally(animationSpec = tween(1000)) { it / 2 },
+ exit = slideOutHorizontally(animationSpec = tween(1000))
+ )
.padding(40.dp)
.border(
width = 2.dp,
diff --git a/tv/tv-foundation/build.gradle b/tv/tv-foundation/build.gradle
index c193d0d..a53a44ee 100644
--- a/tv/tv-foundation/build.gradle
+++ b/tv/tv-foundation/build.gradle
@@ -30,7 +30,7 @@
dependencies {
api(libs.kotlinStdlib)
- def composeVersion = '1.4.0-rc01'
+ def composeVersion = '1.4.2'
implementation(libs.kotlinStdlibCommon)
implementation("androidx.profileinstaller:profileinstaller:1.3.0")
diff --git a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/TvLazyListItemScopeImpl.kt b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/TvLazyListItemScopeImpl.kt
index 7c56a46..50a37e6 100644
--- a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/TvLazyListItemScopeImpl.kt
+++ b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/TvLazyListItemScopeImpl.kt
@@ -19,7 +19,7 @@
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.runtime.IntState
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.LayoutModifier
import androidx.compose.ui.layout.Measurable
@@ -36,8 +36,8 @@
internal class TvLazyListItemScopeImpl : TvLazyListItemScope {
- private val maxWidthState = mutableStateOf(Int.MAX_VALUE)
- private val maxHeightState = mutableStateOf(Int.MAX_VALUE)
+ private val maxWidthState = mutableIntStateOf(Int.MAX_VALUE)
+ private val maxHeightState = mutableIntStateOf(Int.MAX_VALUE)
fun setMaxSize(width: Int, height: Int) {
maxWidthState.intValue = width
maxHeightState.intValue = height
diff --git a/tv/tv-material/api/public_plus_experimental_current.txt b/tv/tv-material/api/public_plus_experimental_current.txt
index 12488a4..093044e 100644
--- a/tv/tv-material/api/public_plus_experimental_current.txt
+++ b/tv/tv-material/api/public_plus_experimental_current.txt
@@ -114,24 +114,8 @@
field public static final long TimeToDisplayItemMillis = 5000L; // 0x1388L
}
- @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CarouselItemDefaults {
- method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformEndToStart();
- method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformLeftToRight();
- method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformRightToLeft();
- method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformStartToEnd();
- property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformEndToStart;
- property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformLeftToRight;
- property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformRightToLeft;
- property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformStartToEnd;
- field public static final androidx.tv.material3.CarouselItemDefaults INSTANCE;
- }
-
public final class CarouselKt {
- method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Carousel(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.tv.material3.CarouselState carouselState, optional long autoScrollDurationMillis, optional androidx.compose.animation.ContentTransform contentTransformStartToEnd, optional androidx.compose.animation.ContentTransform contentTransformEndToStart, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> carouselIndicator, kotlin.jvm.functions.Function2<? super androidx.tv.material3.CarouselScope,? super java.lang.Integer,kotlin.Unit> content);
- }
-
- @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CarouselScope {
- method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public void CarouselItem(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> background, optional androidx.compose.animation.ContentTransform contentTransformStartToEnd, optional androidx.compose.animation.ContentTransform contentTransformEndToStart, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Carousel(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.tv.material3.CarouselState carouselState, optional long autoScrollDurationMillis, optional androidx.compose.animation.ContentTransform contentTransformStartToEnd, optional androidx.compose.animation.ContentTransform contentTransformEndToStart, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> carouselIndicator, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super java.lang.Integer,kotlin.Unit> content);
}
@androidx.compose.runtime.Stable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CarouselState {
diff --git a/tv/tv-material/build.gradle b/tv/tv-material/build.gradle
index 0b86754..334a3f1 100644
--- a/tv/tv-material/build.gradle
+++ b/tv/tv-material/build.gradle
@@ -27,19 +27,10 @@
dependencies {
api(libs.kotlinStdlib)
- def composeVersion = '1.4.0-rc01'
-
- api("androidx.annotation:annotation:1.5.0")
- api("androidx.compose.runtime:runtime:$composeVersion")
+ def composeVersion = '1.4.2'
api(project(":compose:animation:animation"))
- api("androidx.compose.ui:ui:$composeVersion")
- api("androidx.compose.foundation:foundation:$composeVersion")
- api("androidx.compose.foundation:foundation-layout:$composeVersion")
api("androidx.compose.material:material-icons-core:$composeVersion")
- api("androidx.compose.ui:ui-graphics:$composeVersion")
- api(project(":compose:ui:ui-text"))
- api("androidx.compose.ui:ui-util:$composeVersion")
api(project(":tv:tv-foundation"))
implementation(libs.kotlinStdlibCommon)
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt
deleted file mode 100644
index 2f80311..0000000
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt
+++ /dev/null
@@ -1,121 +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.tv.material3
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.text.BasicText
-import androidx.compose.runtime.Composable
-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.focus.onFocusChanged
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.input.key.NativeKeyEvent
-import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.semantics.SemanticsActions
-import androidx.compose.ui.test.assertIsFocused
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithTag
-import androidx.compose.ui.test.performSemanticsAction
-import androidx.compose.ui.unit.dp
-import androidx.test.platform.app.InstrumentationRegistry
-import org.junit.Rule
-import org.junit.Test
-
-const val sampleButtonTag = "sample-button"
-
-class CarouselScopeTest {
- @get:Rule
- val rule = createComposeRule()
-
- @OptIn(ExperimentalTvMaterial3Api::class)
- @Test
- fun carouselItem_parentContainerGainsFocused_onBackPress() {
- val containerBoxTag = "container-box"
- val carouselItemTag = "carousel-item"
-
- rule.setContent {
- val carouselState = remember { CarouselState() }
- var isContainerBoxFocused by remember { mutableStateOf(false) }
- Box(
- modifier = Modifier
- .testTag(containerBoxTag)
- .fillMaxSize()
- .onFocusChanged { isContainerBoxFocused = it.isFocused }
- .border(10.dp, if (isContainerBoxFocused) Color.Green else Color.Transparent)
- .focusable()
- ) {
- CarouselScope(carouselState = carouselState)
- .CarouselItem(
- modifier = Modifier
- .testTag(carouselItemTag),
- background = {
- Box(
- modifier = Modifier
- .size(300.dp)
- .background(Color.Cyan))
- },
- content = { SampleButton() },
- )
- }
- }
-
- // Request focus for Carousel Item on start
- rule.onNodeWithTag(carouselItemTag)
- .performSemanticsAction(SemanticsActions.RequestFocus)
- rule.waitForIdle()
-
- // Check if overlay button in carousel item is focused
- rule.onNodeWithTag(sampleButtonTag, useUnmergedTree = true)
- .assertIsFocused()
-
- // Trigger back press
- performKeyPress(NativeKeyEvent.KEYCODE_BACK)
- rule.waitForIdle()
-
- // Check if carousel item loses focus and parent container gains focus
- rule.onNodeWithTag(containerBoxTag).assertIsFocused()
- }
-
- private fun performKeyPress(keyCode: Int, count: Int = 1) {
- repeat(count) {
- InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keyCode)
- }
- }
-}
-
-@Composable
-private fun SampleButton(text: String = sampleButtonTag) {
- var isFocused by remember { mutableStateOf(false) }
- BasicText(
- text = text,
- modifier = Modifier
- .testTag(text)
- .size(100.dp, 20.dp)
- .background(Color.Yellow)
- .onFocusChanged { isFocused = it.isFocused }
- .border(2.dp, if (isFocused) Color.Green else Color.Transparent)
- .focusable()
- )
-}
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt
index 1c863ef..b6c31ca8 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt
@@ -18,8 +18,10 @@
import android.os.SystemClock
import android.view.KeyEvent
-import androidx.compose.animation.ContentTransform
+import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.slideInHorizontally
+import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.focusable
@@ -75,7 +77,7 @@
private const val delayBetweenItems = 2500L
private const val animationTime = 900L
-@OptIn(ExperimentalTvMaterial3Api::class)
+@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
class CarouselTest {
@get:Rule
val rule = createComposeRule()
@@ -433,7 +435,13 @@
autoScrollDurationMillis = delayBetweenItems
) {
SampleCarouselItem(index = it) {
- Box {
+ Box(
+ modifier = Modifier
+ .animateEnterExit(
+ enter = slideInHorizontally(),
+ exit = slideOutHorizontally()
+ )
+ ) {
Column(modifier = Modifier.align(Alignment.BottomStart)) {
BasicText(text = "carousel-frame")
Row {
@@ -829,7 +837,7 @@
carouselState: CarouselState = remember { CarouselState() },
itemCount: Int = 3,
timeToDisplayItemMillis: Long = delayBetweenItems,
- content: @Composable CarouselScope.(index: Int) -> Unit
+ content: @Composable AnimatedContentScope.(index: Int) -> Unit
) {
Carousel(
modifier = Modifier
@@ -856,24 +864,16 @@
@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
@Composable
-private fun CarouselScope.SampleCarouselItem(
+private fun AnimatedContentScope.SampleCarouselItem(
index: Int,
modifier: Modifier = Modifier,
- contentTransformStartToEnd: ContentTransform =
- CarouselItemDefaults.contentTransformStartToEnd,
- content: (@Composable () -> Unit) = { SampleButton("Play $index") },
+ content: (@Composable AnimatedContentScope.() -> Unit) = { SampleButton("Play $index") },
) {
- CarouselItem(
- modifier = modifier,
- contentTransformStartToEnd = contentTransformStartToEnd,
- background = {
- Box(
- modifier = Modifier
- .fillMaxSize()
- .background(Color.Red)
- .border(2.dp, Color.Blue)
- )
- }
+ Box(
+ modifier = modifier
+ .fillMaxSize()
+ .background(Color.Red)
+ .border(2.dp, Color.Blue)
) {
content()
}
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/TextTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/TextTest.kt
index 5293277..3969b0a 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/TextTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/TextTest.kt
@@ -133,7 +133,6 @@
@Test
fun settingParametersExplicitly() {
- var textColor: Color? = null
var textAlign: TextAlign? = null
var fontSize: TextUnit? = null
var fontStyle: FontStyle? = null
@@ -155,7 +154,6 @@
fontStyle = expectedFontStyle,
letterSpacing = expectedLetterSpacing,
onTextLayout = {
- textColor = it.layoutInput.style.color
textAlign = it.layoutInput.style.textAlign
fontSize = it.layoutInput.style.fontSize
fontStyle = it.layoutInput.style.fontStyle
@@ -168,7 +166,6 @@
rule.runOnIdle {
// explicit parameters should override values from the style.
- Truth.assertThat(textColor).isEqualTo(expectedColor)
Truth.assertThat(textAlign).isEqualTo(expectedTextAlign)
Truth.assertThat(fontSize).isEqualTo(expectedFontSize)
Truth.assertThat(fontStyle).isEqualTo(expectedFontStyle)
@@ -179,7 +176,6 @@
// Not really an expected use-case, but we should ensure the behavior here is consistent.
@Test
fun settingColorAndTextStyle() {
- var textColor: Color? = null
var textAlign: TextAlign? = null
var fontSize: TextUnit? = null
var fontStyle: FontStyle? = null
@@ -202,7 +198,6 @@
letterSpacing = expectedLetterSpacing,
style = ExpectedTextStyle,
onTextLayout = {
- textColor = it.layoutInput.style.color
textAlign = it.layoutInput.style.textAlign
fontSize = it.layoutInput.style.fontSize
fontStyle = it.layoutInput.style.fontStyle
@@ -215,7 +210,6 @@
rule.runOnIdle {
// explicit parameters should override values from the style.
- Truth.assertThat(textColor).isEqualTo(expectedColor)
Truth.assertThat(textAlign).isEqualTo(expectedTextAlign)
Truth.assertThat(fontSize).isEqualTo(expectedFontSize)
Truth.assertThat(fontStyle).isEqualTo(expectedFontStyle)
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
index c8b28a0..001aed4 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
@@ -17,6 +17,7 @@
package androidx.tv.material3
import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.ContentTransform
import androidx.compose.animation.ExperimentalAnimationApi
@@ -113,7 +114,7 @@
.padding(16.dp),
)
},
- content: @Composable CarouselScope.(index: Int) -> Unit
+ content: @Composable AnimatedContentScope.(index: Int) -> Unit
) {
CarouselStateUpdater(carouselState, itemCount)
var focusState: FocusState? by remember { mutableStateOf(null) }
@@ -176,8 +177,7 @@
// IndexOutOfBoundsException. Guarding against this by checking against itemCount
// before invoking.
if (itemCount > 0) {
- CarouselScope(carouselState = carouselState)
- .content(if (activeItemIndex < itemCount) activeItemIndex else 0)
+ content(if (activeItemIndex < itemCount) activeItemIndex else 0)
}
}
this.carouselIndicator()
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt b/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt
deleted file mode 100644
index d0b896a..0000000
--- a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt
+++ /dev/null
@@ -1,189 +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.tv.material3
-
-import android.content.Context
-import android.view.accessibility.AccessibilityManager
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.ContentTransform
-import androidx.compose.animation.ExperimentalAnimationApi
-import androidx.compose.animation.slideInHorizontally
-import androidx.compose.animation.slideOutHorizontally
-import androidx.compose.animation.togetherWith
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.layout.Box
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusDirection
-import androidx.compose.ui.focus.FocusState
-import androidx.compose.ui.focus.onFocusChanged
-import androidx.compose.ui.input.key.onKeyEvent
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalFocusManager
-import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.semantics.CollectionItemInfo
-import androidx.compose.ui.semantics.collectionItemInfo
-import androidx.compose.ui.semantics.isContainer
-import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.tv.material3.KeyEventPropagation.ContinuePropagation
-
-/**
- * This composable is intended for use in Carousel.
- * A composable that has
- * - a [background] layer that is rendered as soon as the composable is visible.
- * - a [content] layer that is rendered on top of the [background]
- *
- * @param background composable defining the background of the item
- * @param itemIndex current active item index of the carousel
- * @param modifier modifier applied to the CarouselItem
- * @param contentTransform content transform to be applied to the content of the item when
- * scrolling
- * @param content composable defining the content displayed on top of the background
- */
-@Suppress("IllegalExperimentalApiUsage")
-@OptIn(ExperimentalAnimationApi::class, ExperimentalComposeUiApi::class)
-@ExperimentalTvMaterial3Api
-@Composable
-internal fun CarouselItem(
- itemIndex: Int,
- modifier: Modifier = Modifier,
- background: @Composable () -> Unit = {},
- contentTransform: ContentTransform =
- CarouselItemDefaults.contentTransformStartToEnd,
- content: @Composable () -> Unit,
-) {
- val context = LocalContext.current
- val accessibilityManager = remember {
- context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
- }
- var containerBoxFocusState: FocusState? by remember { mutableStateOf(null) }
- val focusManager = LocalFocusManager.current
- var exitFocus by remember { mutableStateOf(false) }
-
- var isVisible by remember { mutableStateOf(false) }
-
- DisposableEffect(itemIndex) {
- isVisible = true
- onDispose { isVisible = false }
- }
-
- // This box holds the focus until the overlay animation completes
- Box(
- modifier = modifier
- .semantics(mergeDescendants = true) {
- @Suppress("DEPRECATION")
- isContainer = true
- collectionItemInfo =
- CollectionItemInfo(
- rowIndex = 0,
- rowSpan = 1,
- columnIndex = itemIndex,
- columnSpan = 1
- )
- }
- .onKeyEvent {
- exitFocus = it.isBackPress() && it.isTypeKeyDown()
- ContinuePropagation
- }
- .onFocusChanged {
- containerBoxFocusState = it
- if (it.isFocused && exitFocus) {
- focusManager.moveFocus(FocusDirection.Exit)
- exitFocus = false
- }
- }
- .then(
- if (accessibilityManager.isEnabled)
- Modifier.clickable {
- focusManager.moveFocus(FocusDirection.Enter)
- }
- else
- Modifier.focusable()
- )
- ) {
- background()
-
- AnimatedVisibility(
- visible = isVisible,
- enter = contentTransform.targetContentEnter,
- exit = contentTransform.initialContentExit,
- ) {
- LaunchedEffect(transition.isRunning, containerBoxFocusState?.isFocused) {
- if (!transition.isRunning &&
- containerBoxFocusState?.isFocused == true &&
- !accessibilityManager.isEnabled
- ) {
- focusManager.moveFocus(FocusDirection.Enter)
- }
- }
- content.invoke()
- }
- }
-}
-
-@ExperimentalTvMaterial3Api
-object CarouselItemDefaults {
- /**
- * Transform the content from right to left
- */
- // Keeping this as public so that users can access it directly without the isLTR helper
- val contentTransformRightToLeft: ContentTransform
- @Composable get() =
- slideInHorizontally { it * 4 }
- .togetherWith(slideOutHorizontally { it * 4 })
-
- /**
- * Transform the content from left to right
- */
- // Keeping this as public so that users can access it directly without the isLTR helper
- val contentTransformLeftToRight: ContentTransform
- @Composable get() =
- slideInHorizontally()
- .togetherWith(slideOutHorizontally())
-
- /**
- * Content transform applied when moving forward taking isLTR into account
- */
- val contentTransformStartToEnd
- @Composable get() =
- if (isLtr())
- contentTransformRightToLeft
- else
- contentTransformLeftToRight
-
- /**
- * Content transform applied when moving backward taking isLTR into account
- */
- val contentTransformEndToStart
- @Composable get() =
- if (isLtr())
- contentTransformLeftToRight
- else
- contentTransformRightToLeft
-}
-
-@Composable
-private fun isLtr() = LocalLayoutDirection.current == LayoutDirection.Ltr
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselScope.kt b/tv/tv-material/src/main/java/androidx/tv/material3/CarouselScope.kt
deleted file mode 100644
index 665ceb9..0000000
--- a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselScope.kt
+++ /dev/null
@@ -1,66 +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.tv.material3
-
-import androidx.compose.animation.ContentTransform
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-
-/**
- * CarouselScope provides a [CarouselScope.CarouselItem] function which you can use to
- * provide the carousel item's animation, background and the inner content.
- */
-@ExperimentalTvMaterial3Api
-class CarouselScope @OptIn(ExperimentalTvMaterial3Api::class)
-internal constructor(private val carouselState: CarouselState) {
- /**
- * [CarouselScope.CarouselItem] can be used to define a item's animation, background, and
- * content. Using this is optional and you can choose to define your own CarouselItem from
- * scratch
- *
- * @param modifier modifier applied to the CarouselItem
- * @param background composable defining the background of the item
- * @param contentTransformStartToEnd content transform to be applied to the content of the item
- * when scrolling forward in the carousel
- * @param contentTransformEndToStart content transform to be applied to the content of the item
- * when scrolling backward in the carousel
- * @param content composable defining the content displayed on top of the background
- */
- @Composable
- @ExperimentalTvMaterial3Api
- fun CarouselItem(
- modifier: Modifier = Modifier,
- background: @Composable () -> Unit = {},
- contentTransformStartToEnd: ContentTransform =
- CarouselItemDefaults.contentTransformStartToEnd,
- contentTransformEndToStart: ContentTransform =
- CarouselItemDefaults.contentTransformEndToStart,
- content: @Composable () -> Unit
- ) {
- CarouselItem(
- background = background,
- itemIndex = carouselState.activeItemIndex,
- contentTransform =
- if (carouselState.isMovingBackward)
- contentTransformEndToStart
- else
- contentTransformStartToEnd,
- modifier = modifier,
- content = content,
- )
- }
-}
diff --git a/wear/compose/compose-material-core/src/commonMain/kotlin/androidx/wear/compose/materialcore/Text.kt b/wear/compose/compose-material-core/src/commonMain/kotlin/androidx/wear/compose/materialcore/Text.kt
index c258ffa..2b96e52 100644
--- a/wear/compose/compose-material-core/src/commonMain/kotlin/androidx/wear/compose/materialcore/Text.kt
+++ b/wear/compose/compose-material-core/src/commonMain/kotlin/androidx/wear/compose/materialcore/Text.kt
@@ -99,23 +99,20 @@
onTextLayout: (TextLayoutResult) -> Unit,
style: TextStyle
) {
- val mergedStyle = style.merge(
- TextStyle(
- color = color,
- fontSize = fontSize,
- fontWeight = fontWeight,
- textAlign = textAlign,
- lineHeight = lineHeight,
- fontFamily = fontFamily,
- textDecoration = textDecoration,
- fontStyle = fontStyle,
- letterSpacing = letterSpacing
- )
- )
BasicText(
text = text,
modifier = modifier,
- style = mergedStyle,
+ style = style.merge(
+ color = color,
+ fontSize = fontSize,
+ fontWeight = fontWeight,
+ textAlign = textAlign,
+ lineHeight = lineHeight,
+ fontFamily = fontFamily,
+ textDecoration = textDecoration,
+ fontStyle = fontStyle,
+ letterSpacing = letterSpacing
+ ),
onTextLayout = onTextLayout,
overflow = overflow,
softWrap = softWrap,
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Placeholder.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Placeholder.kt
index 53ed85a..141db5a 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Placeholder.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Placeholder.kt
@@ -24,6 +24,7 @@
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
@@ -253,7 +254,7 @@
* The frame time in milliseconds in the calling context of frame dispatch. Used to coordinate
* the placeholder state and effects. Usually provided by [withInfiniteAnimationFrameMillis].
*/
- internal val frameMillis = mutableStateOf(0L)
+ internal val frameMillis = mutableLongStateOf(0L)
private var startOfWipeOffAnimation = 0L
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Swipeable.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Swipeable.kt
index e030378..52ed98b 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Swipeable.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Swipeable.kt
@@ -29,6 +29,7 @@
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.Saver
@@ -116,12 +117,12 @@
@ExperimentalWearMaterialApi
val overflow: State<Float> get() = overflowState
- private val offsetState = mutableStateOf(0f)
- private val overflowState = mutableStateOf(0f)
+ private val offsetState = mutableFloatStateOf(0f)
+ private val overflowState = mutableFloatStateOf(0f)
// the source of truth for the "real"(non ui) position
// basically position in bounds + overflow
- private val absoluteOffset = mutableStateOf(0f)
+ private val absoluteOffset = mutableFloatStateOf(0f)
private var initialOffset: Float = 0f
diff --git a/wear/compose/compose-navigation/src/main/java/androidx/wear/compose/navigation/SwipeDismissableNavHost.kt b/wear/compose/compose-navigation/src/main/java/androidx/wear/compose/navigation/SwipeDismissableNavHost.kt
index f541247..1a5e97a 100644
--- a/wear/compose/compose-navigation/src/main/java/androidx/wear/compose/navigation/SwipeDismissableNavHost.kt
+++ b/wear/compose/compose-navigation/src/main/java/androidx/wear/compose/navigation/SwipeDismissableNavHost.kt
@@ -16,6 +16,7 @@
package androidx.wear.compose.navigation
+import android.util.Log
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
@@ -171,9 +172,23 @@
// no WearNavigator.Destinations were added to the navigation backstack (be sure to build
// the NavGraph using androidx.wear.compose.navigation.composable) or because the last entry
// was popped prior to navigating (instead, use navigate with popUpTo).
- val current = if (backStack.isNotEmpty()) backStack.last() else throw IllegalArgumentException(
- "The WearNavigator backstack is empty, there is no navigation destination to display."
- )
+ // If the activity is using FLAG_ACTIVITY_NEW_TASK then it also needs to set
+ // FLAG_ACTIVITY_CLEAR_TASK, otherwise the activity will be created twice,
+ // the first of these with an empty backstack.
+ val current = backStack.lastOrNull()
+
+ if (current == null) {
+ val warningText =
+ "Current backstack entry is empty. Please ensure: \n" +
+ "1. The current WearNavigator navigation backstack is not empty (e.g. by using " +
+ "androidx.wear.compose.navigation.composable to build your nav graph). \n" +
+ "2. The last entry is not popped prior to navigation " +
+ "(instead, use navigate with popUpTo). \n" +
+ "3. If the activity uses FLAG_ACTIVITY_NEW_TASK you should also set " +
+ "FLAG_ACTIVITY_CLEAR_TASK to maintain the backstack consistency."
+
+ Log.w(TAG, warningText)
+ }
val swipeState = state.swipeToDismissBoxState
LaunchedEffect(swipeState.currentValue) {
@@ -200,7 +215,7 @@
modifier = Modifier,
hasBackground = previous != null,
backgroundKey = previous?.id ?: SwipeToDismissKeys.Background,
- contentKey = current.id,
+ contentKey = current?.id ?: SwipeToDismissKeys.Content,
content = { isBackground ->
BoxedStackEntryContent(if (isBackground) previous else current, stateHolder, modifier)
}
@@ -279,3 +294,5 @@
}
}
}
+
+private const val TAG = "SwipeDismissableNavHost"
\ No newline at end of file
diff --git a/wear/protolayout/protolayout-expression-pipeline/api/current.txt b/wear/protolayout/protolayout-expression-pipeline/api/current.txt
index 814cdba..18e01b6 100644
--- a/wear/protolayout/protolayout-expression-pipeline/api/current.txt
+++ b/wear/protolayout/protolayout-expression-pipeline/api/current.txt
@@ -56,6 +56,7 @@
public class StateStore {
method public static androidx.wear.protolayout.expression.pipeline.StateStore create(java.util.Map<java.lang.String!,androidx.wear.protolayout.expression.StateEntryBuilders.StateEntryValue!>);
method @UiThread public void setStateEntryValues(java.util.Map<java.lang.String!,androidx.wear.protolayout.expression.StateEntryBuilders.StateEntryValue!>);
+ field public static final int MAX_STATE_ENTRY_COUNT = 100; // 0x64
}
public interface TimeGateway {
diff --git a/wear/protolayout/protolayout-expression-pipeline/api/public_plus_experimental_current.txt b/wear/protolayout/protolayout-expression-pipeline/api/public_plus_experimental_current.txt
index 814cdba..18e01b6 100644
--- a/wear/protolayout/protolayout-expression-pipeline/api/public_plus_experimental_current.txt
+++ b/wear/protolayout/protolayout-expression-pipeline/api/public_plus_experimental_current.txt
@@ -56,6 +56,7 @@
public class StateStore {
method public static androidx.wear.protolayout.expression.pipeline.StateStore create(java.util.Map<java.lang.String!,androidx.wear.protolayout.expression.StateEntryBuilders.StateEntryValue!>);
method @UiThread public void setStateEntryValues(java.util.Map<java.lang.String!,androidx.wear.protolayout.expression.StateEntryBuilders.StateEntryValue!>);
+ field public static final int MAX_STATE_ENTRY_COUNT = 100; // 0x64
}
public interface TimeGateway {
diff --git a/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt b/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt
index 62aa8aa..6dd8857 100644
--- a/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt
+++ b/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt
@@ -58,6 +58,7 @@
method public static androidx.wear.protolayout.expression.pipeline.StateStore create(java.util.Map<java.lang.String!,androidx.wear.protolayout.expression.StateEntryBuilders.StateEntryValue!>);
method @UiThread public void setStateEntryValues(java.util.Map<java.lang.String!,androidx.wear.protolayout.expression.StateEntryBuilders.StateEntryValue!>);
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @UiThread public void setStateEntryValuesProto(java.util.Map<java.lang.String!,androidx.wear.protolayout.expression.proto.StateEntryProto.StateEntryValue!>);
+ field public static final int MAX_STATE_ENTRY_COUNT = 100; // 0x64
}
public interface TimeGateway {
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/BoolNodes.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/BoolNodes.java
index afe4be6..c95560c 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/BoolNodes.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/BoolNodes.java
@@ -35,8 +35,7 @@
private final DynamicTypeValueReceiverWithPreUpdate<Boolean> mDownstream;
FixedBoolNode(
- FixedBool protoNode,
- DynamicTypeValueReceiverWithPreUpdate<Boolean> downstream) {
+ FixedBool protoNode, DynamicTypeValueReceiverWithPreUpdate<Boolean> downstream) {
mValue = protoNode.getValue();
mDownstream = downstream;
}
@@ -171,6 +170,10 @@
return a && b;
case LOGICAL_OP_TYPE_OR:
return a || b;
+ case LOGICAL_OP_TYPE_EQUAL:
+ return a.equals(b);
+ case LOGICAL_OP_TYPE_NOT_EQUAL:
+ return !a.equals(b);
default:
Log.e(TAG, "Unknown operation type in LogicalBoolOp");
return false;
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/BoundDynamicTypeImpl.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/BoundDynamicTypeImpl.java
index 3ed31ac..bbae3dc 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/BoundDynamicTypeImpl.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/BoundDynamicTypeImpl.java
@@ -16,6 +16,11 @@
package androidx.wear.protolayout.expression.pipeline;
+import android.os.Handler;
+import android.os.Looper;
+
+import androidx.annotation.UiThread;
+
import java.util.List;
/**
@@ -76,6 +81,15 @@
@Override
public void close() {
+ if (Looper.getMainLooper().isCurrentThread()) {
+ closeInternal();
+ } else {
+ new Handler(Looper.getMainLooper()).post(this::closeInternal);
+ }
+ }
+
+ @UiThread
+ private void closeInternal() {
mNodes.stream()
.filter(n -> n instanceof DynamicDataSourceNode)
.forEach(n -> ((DynamicDataSourceNode<?>) n).destroy());
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java
index 068ce4b..3deccea 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java
@@ -250,8 +250,8 @@
/**
* Gets the quota manager used for limiting the total number of dynamic types in the
- * pipeline, or {@code null} if there are no restriction on the number of dynamic types.
- * If present, the quota manager is used to prevent unreasonably expensive expressions.
+ * pipeline, or {@code null} if there are no restriction on the number of dynamic types. If
+ * present, the quota manager is used to prevent unreasonably expensive expressions.
*/
@Nullable
public QuotaManager getDynamicTypesQuotaManager() {
@@ -303,8 +303,8 @@
MainThreadExecutor uiExecutor = new MainThreadExecutor(uiHandler);
TimeGateway timeGateway = config.getTimeGateway();
if (timeGateway == null) {
- timeGateway = new TimeGatewayImpl(uiHandler);
- ((TimeGatewayImpl) timeGateway).enableUpdates();
+ timeGateway = new TimeGatewayImpl(uiHandler);
+ ((TimeGatewayImpl) timeGateway).enableUpdates();
}
this.mTimeDataSource = new EpochTimePlatformDataSource(uiExecutor, timeGateway);
if (config.getSensorGateway() != null) {
@@ -331,7 +331,7 @@
if (!mDynamicTypesQuotaManager.tryAcquireQuota(boundDynamicType.getDynamicNodeCount())) {
throw new EvaluationException(
"Dynamic type expression limit reached. Try making the dynamic type expression"
- + " shorter or reduce the number of dynamic type expressions.");
+ + " shorter or reduce the number of dynamic type expressions.");
}
return boundDynamicType;
}
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/StateStore.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/StateStore.java
index c2ce371..f722bbb 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/StateStore.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/StateStore.java
@@ -18,6 +18,8 @@
import static java.util.stream.Collectors.toMap;
+import android.annotation.SuppressLint;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
@@ -41,13 +43,27 @@
* must only be used from the UI thread.
*/
public class StateStore {
+ /**
+ * Maximum number for state entries allowed for this {@link StateStore}.
+ *
+ * <p>The ProtoLayout state model is not designed to handle large volumes of layout provided
+ * state. So we limit the number of state entries to keep the on-the-wire size and state
+ * store update times manageable.
+ */
+ @SuppressLint("MinMaxConstant")
+ public static final int MAX_STATE_ENTRY_COUNT = 100;
@NonNull private final Map<String, StateEntryValue> mCurrentState = new ArrayMap<>();
@NonNull
private final Map<String, Set<DynamicTypeValueReceiverWithPreUpdate<StateEntryValue>>>
mRegisteredCallbacks = new ArrayMap<>();
- /** Creates a {@link StateStore}. */
+ /**
+ * Creates a {@link StateStore}.
+ *
+ * @throws IllegalStateException if number of initialState entries is greater than
+ * {@link StateStore#MAX_STATE_ENTRY_COUNT}.
+ */
@NonNull
public static StateStore create(
@NonNull Map<String, StateEntryBuilders.StateEntryValue> initialState) {
@@ -56,6 +72,9 @@
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public StateStore(@NonNull Map<String, StateEntryValue> initialState) {
+ if (initialState.size() > MAX_STATE_ENTRY_COUNT) {
+ throw stateTooLargeException(initialState.size());
+ }
mCurrentState.putAll(initialState);
}
@@ -63,6 +82,10 @@
* Sets the given state, replacing the current state.
*
* <p>Informs registered listeners of changed values, invalidates removed values.
+ *
+ * @throws IllegalStateException if number of state entries is greater than
+ * {@link StateStore#MAX_STATE_ENTRY_COUNT}. The state will not update and old state entries
+ * will stay in place.
*/
@UiThread
public void setStateEntryValues(
@@ -74,10 +97,18 @@
* Sets the given state, replacing the current state.
*
* <p>Informs registered listeners of changed values, invalidates removed values.
+ *
+ * @throws IllegalStateException if number of state entries is larger than
+ * {@link StateStore#MAX_STATE_ENTRY_COUNT}. The state will not update and old state entries
+ * will stay in place.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
@UiThread
public void setStateEntryValuesProto(@NonNull Map<String, StateEntryValue> newState) {
+ if (newState.size() > MAX_STATE_ENTRY_COUNT) {
+ throw stateTooLargeException(newState.size());
+ }
+
// Figure out which nodes have actually changed.
Set<String> removedKeys = getRemovedKeys(newState);
Map<String, StateEntryValue> changedEntries = getChangedEntries(newState);
@@ -85,10 +116,9 @@
Stream.concat(removedKeys.stream(), changedEntries.keySet().stream())
.forEach(
key -> {
- for (DynamicTypeValueReceiverWithPreUpdate<StateEntryValue>
- callback :
- mRegisteredCallbacks.getOrDefault(
- key, Collections.emptySet())) {
+ for (DynamicTypeValueReceiverWithPreUpdate<StateEntryValue> callback :
+ mRegisteredCallbacks.getOrDefault(
+ key, Collections.emptySet())) {
callback.onPreUpdate();
}
});
@@ -168,4 +198,12 @@
}
return result;
}
+
+ static IllegalStateException stateTooLargeException(int stateSize) {
+ return new IllegalStateException(
+ String.format(
+ "Too many state entries: %d. The maximum number of allowed state entries "
+ + "is %d.",
+ stateSize, MAX_STATE_ENTRY_COUNT));
+ }
}
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/TimeGatewayImpl.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/TimeGatewayImpl.java
index f9f699d..18bae95 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/TimeGatewayImpl.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/TimeGatewayImpl.java
@@ -138,6 +138,7 @@
}
@Override
+ @UiThread
public void close() {
setUpdatesEnabled(false);
registeredCallbacks.clear();
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/BoolNodesTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/BoolNodesTest.java
new file mode 100644
index 0000000..e5f987c
--- /dev/null
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/BoolNodesTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.wear.protolayout.expression.pipeline;
+
+import static androidx.wear.protolayout.expression.proto.DynamicProto.LogicalOpType.LOGICAL_OP_TYPE_AND;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.LogicalOpType.LOGICAL_OP_TYPE_EQUAL;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.LogicalOpType.LOGICAL_OP_TYPE_NOT_EQUAL;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.LogicalOpType.LOGICAL_OP_TYPE_OR;
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.wear.protolayout.expression.pipeline.BoolNodes.FixedBoolNode;
+import androidx.wear.protolayout.expression.pipeline.BoolNodes.StateBoolNode;
+import androidx.wear.protolayout.expression.proto.DynamicProto;
+import androidx.wear.protolayout.expression.proto.DynamicProto.LogicalBoolOp;
+import androidx.wear.protolayout.expression.proto.DynamicProto.StateBoolSource;
+import androidx.wear.protolayout.expression.proto.FixedProto.FixedBool;
+import androidx.wear.protolayout.expression.proto.StateEntryProto.StateEntryValue;
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class BoolNodesTest {
+ @Test
+ public void fixedBoolNodeTest() {
+ List<Boolean> results = new ArrayList<>();
+
+ FixedBool protoNode = FixedBool.newBuilder().setValue(false).build();
+ FixedBoolNode node = new FixedBoolNode(protoNode, new AddToListCallback<>(results));
+
+ node.preInit();
+ node.init();
+
+ assertThat(results).containsExactly(false);
+ }
+
+ @Test
+ public void stateBoolNodeTest() {
+ List<Boolean> results = new ArrayList<>();
+ StateStore oss =
+ new StateStore(
+ ImmutableMap.of(
+ "foo",
+ StateEntryValue.newBuilder()
+ .setBoolVal(FixedBool.newBuilder().setValue(true))
+ .build()));
+
+ StateBoolSource protoNode = StateBoolSource.newBuilder().setSourceKey("foo").build();
+ StateBoolNode node = new StateBoolNode(oss, protoNode, new AddToListCallback<>(results));
+
+ node.preInit();
+ node.init();
+
+ assertThat(results).containsExactly(true);
+ }
+
+ @Test
+ public void stateBoolUpdatesWithStateChanges() {
+ List<Boolean> results = new ArrayList<>();
+ StateStore oss =
+ new StateStore(
+ ImmutableMap.of(
+ "foo",
+ StateEntryValue.newBuilder()
+ .setBoolVal(FixedBool.newBuilder().setValue(true))
+ .build()));
+
+ StateBoolSource protoNode = StateBoolSource.newBuilder().setSourceKey("foo").build();
+ StateBoolNode node = new StateBoolNode(oss, protoNode, new AddToListCallback<>(results));
+
+ node.preInit();
+ node.init();
+
+ results.clear();
+
+ oss.setStateEntryValuesProto(
+ ImmutableMap.of(
+ "foo",
+ StateEntryValue.newBuilder()
+ .setBoolVal(FixedBool.newBuilder().setValue(false))
+ .build()));
+
+ assertThat(results).containsExactly(false);
+ }
+
+ @Test
+ public void stateBoolNoUpdatesAfterDestroy() {
+ List<Boolean> results = new ArrayList<>();
+ StateStore oss =
+ new StateStore(
+ ImmutableMap.of(
+ "foo",
+ StateEntryValue.newBuilder()
+ .setBoolVal(FixedBool.newBuilder().setValue(false))
+ .build()));
+
+ StateBoolSource protoNode = StateBoolSource.newBuilder().setSourceKey("foo").build();
+ StateBoolNode node = new StateBoolNode(oss, protoNode, new AddToListCallback<>(results));
+
+ node.preInit();
+ node.init();
+ assertThat(results).containsExactly(false);
+
+ results.clear();
+ node.destroy();
+ oss.setStateEntryValuesProto(
+ ImmutableMap.of(
+ "foo",
+ StateEntryValue.newBuilder()
+ .setBoolVal(FixedBool.newBuilder().setValue(true))
+ .build()));
+ assertThat(results).isEmpty();
+ }
+
+ @Test
+ public void logicalBoolOpTest() {
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_AND, true, true)).isTrue();
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_AND, true, false)).isFalse();
+
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_OR, true, false)).isTrue();
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_OR, false, false)).isFalse();
+
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_EQUAL, true, true)).isTrue();
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_EQUAL, true, false)).isFalse();
+
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_NOT_EQUAL, true, false)).isTrue();
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_NOT_EQUAL, false, false)).isFalse();
+ }
+
+ private static boolean evaluateLogicalOperation(
+ DynamicProto.LogicalOpType logicalOpType, boolean lhs, boolean rhs) {
+ List<Boolean> results = new ArrayList<>();
+
+ LogicalBoolOp protoNode = LogicalBoolOp.newBuilder().setOperationType(logicalOpType).build();
+ BoolNodes.LogicalBoolOp node =
+ new BoolNodes.LogicalBoolOp(protoNode, new AddToListCallback<>(results));
+
+ FixedBool lhsProtoNode = FixedBool.newBuilder().setValue(lhs).build();
+ FixedBoolNode lhsNode = new FixedBoolNode(lhsProtoNode, node.getLhsIncomingCallback());
+ lhsNode.init();
+
+ FixedBool rhsProtoNode = FixedBool.newBuilder().setValue(rhs).build();
+ FixedBoolNode rhsNode = new FixedBoolNode(rhsProtoNode, node.getRhsIncomingCallback());
+ rhsNode.init();
+
+ return results.get(0);
+ }
+}
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/ParametrizedDynamicTypeEvaluatorTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/ParametrizedDynamicTypeEvaluatorTest.java
index 7632f30..2f62ea9 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/ParametrizedDynamicTypeEvaluatorTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/ParametrizedDynamicTypeEvaluatorTest.java
@@ -132,6 +132,10 @@
test(DynamicFloat.constant(0.6f).gte(0.4f), true),
test(DynamicFloat.constant(0.1234568f).gte(0.1234562f), true),
test(DynamicBool.constant(true), true),
+ test(DynamicBool.constant(true).eq(DynamicBool.constant(true)), true),
+ test(DynamicBool.constant(true).eq(DynamicBool.constant(false)), false),
+ test(DynamicBool.constant(true).ne(DynamicBool.constant(true)), false),
+ test(DynamicBool.constant(true).ne(DynamicBool.constant(false)), true),
test(DynamicBool.constant(true).negate(), false),
test(DynamicBool.constant(false).negate(), true),
test(DynamicBool.constant(true).and(DynamicBool.constant(true)), true),
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/StateStoreTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/StateStoreTest.java
index d3a2bb4..ae1630d 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/StateStoreTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/StateStoreTest.java
@@ -16,6 +16,7 @@
package androidx.wear.protolayout.expression.pipeline;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -38,6 +39,9 @@
import org.mockito.InOrder;
import org.mockito.Mockito;
+import java.util.HashMap;
+import java.util.Map;
+
@RunWith(AndroidJUnit4.class)
public class StateStoreTest {
@Rule public Expect mExpect = Expect.create();
@@ -48,6 +52,8 @@
"foo", buildStateEntry("bar"),
"baz", buildStateEntry("foobar")));
+ public StateStoreTest() {}
+
@Test
public void setBuilderApi() {
mStateStoreUnderTest.setStateEntryValues(
@@ -58,6 +64,25 @@
}
@Test
+ public void initState_largeNumberOfEntries_throws() {
+ Map<String, StateEntryBuilders.StateEntryValue> state = new HashMap<>();
+ for (int i = 0; i < StateStore.MAX_STATE_ENTRY_COUNT + 10; i++) {
+ state.put(Integer.toString(i), StateEntryBuilders.StateEntryValue.fromString("baz"));
+ }
+ assertThrows(IllegalStateException.class, () -> StateStore.create(state));
+ }
+
+ @Test
+ public void newState_largeNumberOfEntries_throws() {
+ Map<String, StateEntryBuilders.StateEntryValue> state = new HashMap<>();
+ for (int i = 0; i < StateStore.MAX_STATE_ENTRY_COUNT + 10; i++) {
+ state.put(Integer.toString(i), StateEntryBuilders.StateEntryValue.fromString("baz"));
+ }
+ assertThrows(
+ IllegalStateException.class, () -> mStateStoreUnderTest.setStateEntryValues(state));
+ }
+
+ @Test
public void canReadInitialState() {
mExpect.that(mStateStoreUnderTest.getStateEntryValuesProto("foo"))
.isEqualTo(buildStateEntry("bar"));
@@ -88,8 +113,7 @@
@Test
public void setStateFiresListeners() {
- DynamicTypeValueReceiverWithPreUpdate<StateEntryValue> cb =
- buildStateUpdateCallbackMock();
+ DynamicTypeValueReceiverWithPreUpdate<StateEntryValue> cb = buildStateUpdateCallbackMock();
mStateStoreUnderTest.registerCallback("foo", cb);
mStateStoreUnderTest.setStateEntryValuesProto(
@@ -101,8 +125,7 @@
@Test
public void setStateFiresOnPreStateUpdateFirst() {
- DynamicTypeValueReceiverWithPreUpdate<StateEntryValue> cb =
- buildStateUpdateCallbackMock();
+ DynamicTypeValueReceiverWithPreUpdate<StateEntryValue> cb = buildStateUpdateCallbackMock();
InOrder inOrder = Mockito.inOrder(cb);
@@ -166,8 +189,7 @@
@SuppressWarnings("unchecked")
@Test
public void canUnregisterListeners() {
- DynamicTypeValueReceiverWithPreUpdate<StateEntryValue> cb =
- buildStateUpdateCallbackMock();
+ DynamicTypeValueReceiverWithPreUpdate<StateEntryValue> cb = buildStateUpdateCallbackMock();
mStateStoreUnderTest.registerCallback("foo", cb);
mStateStoreUnderTest.setStateEntryValuesProto(
@@ -183,8 +205,7 @@
}
@SuppressWarnings("unchecked")
- private DynamicTypeValueReceiverWithPreUpdate<StateEntryValue>
- buildStateUpdateCallbackMock() {
+ private DynamicTypeValueReceiverWithPreUpdate<StateEntryValue> buildStateUpdateCallbackMock() {
// This needs an unchecked cast because of the generic; this method just centralizes the
// warning suppression.
return mock(DynamicTypeValueReceiverWithPreUpdate.class);
diff --git a/wear/protolayout/protolayout-expression/api/current.txt b/wear/protolayout/protolayout-expression/api/current.txt
index 973a583..b4f26d0 100644
--- a/wear/protolayout/protolayout-expression/api/current.txt
+++ b/wear/protolayout/protolayout-expression/api/current.txt
@@ -97,8 +97,10 @@
public static interface DynamicBuilders.DynamicBool extends androidx.wear.protolayout.expression.DynamicBuilders.DynamicType {
method public default androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool and(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method public static androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool constant(boolean);
+ method public default androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool eq(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method public static androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool fromByteArray(byte[]);
method public static androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool fromState(String);
+ method public default androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool ne(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method public default androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool negate();
method public default androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool or(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method public default byte[] toDynamicBoolByteArray();
diff --git a/wear/protolayout/protolayout-expression/api/public_plus_experimental_current.txt b/wear/protolayout/protolayout-expression/api/public_plus_experimental_current.txt
index 0864423..adf05da 100644
--- a/wear/protolayout/protolayout-expression/api/public_plus_experimental_current.txt
+++ b/wear/protolayout/protolayout-expression/api/public_plus_experimental_current.txt
@@ -97,8 +97,10 @@
public static interface DynamicBuilders.DynamicBool extends androidx.wear.protolayout.expression.DynamicBuilders.DynamicType {
method public default androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool and(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method public static androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool constant(boolean);
+ method public default androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool eq(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method public static androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool fromByteArray(byte[]);
method public static androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool fromState(String);
+ method public default androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool ne(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method public default androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool negate();
method public default androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool or(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method public default byte[] toDynamicBoolByteArray();
diff --git a/wear/protolayout/protolayout-expression/api/restricted_current.txt b/wear/protolayout/protolayout-expression/api/restricted_current.txt
index 973a583..b4f26d0 100644
--- a/wear/protolayout/protolayout-expression/api/restricted_current.txt
+++ b/wear/protolayout/protolayout-expression/api/restricted_current.txt
@@ -97,8 +97,10 @@
public static interface DynamicBuilders.DynamicBool extends androidx.wear.protolayout.expression.DynamicBuilders.DynamicType {
method public default androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool and(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method public static androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool constant(boolean);
+ method public default androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool eq(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method public static androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool fromByteArray(byte[]);
method public static androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool fromState(String);
+ method public default androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool ne(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method public default androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool negate();
method public default androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool or(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method public default byte[] toDynamicBoolByteArray();
diff --git a/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/DynamicBuilders.java b/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/DynamicBuilders.java
index debe91bc..9374277 100644
--- a/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/DynamicBuilders.java
+++ b/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/DynamicBuilders.java
@@ -265,7 +265,13 @@
* @since 1.2
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
- @IntDef({LOGICAL_OP_TYPE_UNDEFINED, LOGICAL_OP_TYPE_AND, LOGICAL_OP_TYPE_OR})
+ @IntDef({
+ LOGICAL_OP_TYPE_UNDEFINED,
+ LOGICAL_OP_TYPE_AND,
+ LOGICAL_OP_TYPE_OR,
+ LOGICAL_OP_TYPE_EQUAL,
+ LOGICAL_OP_TYPE_NOT_EQUAL
+ })
@Retention(RetentionPolicy.SOURCE)
@interface LogicalOpType {}
@@ -291,6 +297,20 @@
static final int LOGICAL_OP_TYPE_OR = 2;
/**
+ * Equal check.
+ *
+ * @since 1.2
+ */
+ static final int LOGICAL_OP_TYPE_EQUAL = 3;
+
+ /**
+ * Not Equal check.
+ *
+ * @since 1.2
+ */
+ static final int LOGICAL_OP_TYPE_NOT_EQUAL = 4;
+
+ /**
* The duration part to retrieve using {@link GetDurationPartOp}.
*
* @since 1.2
@@ -2334,8 +2354,8 @@
/**
* Sets minimum number of integer digits for the formatter. Defaults to one if not
- * specified. If minIntegerDigits is zero and the -1 < input < 1, the Integer
- * part will not appear.
+ * specified. If minIntegerDigits is zero and the -1 < input < 1, the Integer part
+ * will not appear.
*/
@NonNull
public Builder setMinIntegerDigits(@IntRange(from = 0) int minIntegerDigits) {
@@ -2643,7 +2663,6 @@
return fromProto(proto, null);
}
-
@NonNull
DynamicProto.StateStringSource toProto() {
return mImpl;
@@ -4780,8 +4799,8 @@
/**
* Sets minimum number of integer digits for the formatter. Defaults to one if not
- * specified. If minIntegerDigits is zero and the -1 < input < 1, the Integer
- * part will not appear.
+ * specified. If minIntegerDigits is zero and the -1 < input < 1, the Integer part
+ * will not appear.
*/
@NonNull
public Builder setMinIntegerDigits(@IntRange(from = 0) int minIntegerDigits) {
@@ -5607,6 +5626,32 @@
.build();
}
+ /**
+ * Returns a {@link DynamicBool} that is true if the value of this {@link DynamicBool} and
+ * {@code other} are equal, otherwise it's false.
+ */
+ @NonNull
+ default DynamicBool eq(@NonNull DynamicBool other) {
+ return new LogicalBoolOp.Builder()
+ .setInputLhs(this)
+ .setInputRhs(other)
+ .setOperationType(DynamicBuilders.LOGICAL_OP_TYPE_EQUAL)
+ .build();
+ }
+
+ /**
+ * Returns a {@link DynamicBool} that is true if the value of this {@link DynamicBool} and
+ * {@code other} are not equal, otherwise it's false.
+ */
+ @NonNull
+ default DynamicBool ne(@NonNull DynamicBool other) {
+ return new LogicalBoolOp.Builder()
+ .setInputLhs(this)
+ .setInputRhs(other)
+ .setOperationType(DynamicBuilders.LOGICAL_OP_TYPE_NOT_EQUAL)
+ .build();
+ }
+
/** Get the fingerprint for this object or null if unknown. */
@RestrictTo(Scope.LIBRARY_GROUP)
@Nullable
@@ -5622,7 +5667,6 @@
}
}
-
/** Creates a new wrapper instance from the proto. */
@RestrictTo(Scope.LIBRARY_GROUP)
@NonNull
@@ -6366,7 +6410,6 @@
}
}
-
/** Creates a new wrapper instance from the proto. */
@RestrictTo(Scope.LIBRARY_GROUP)
@NonNull
diff --git a/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/DynamicBoolTest.java b/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/DynamicBoolTest.java
index b9bf7e3..beb1207 100644
--- a/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/DynamicBoolTest.java
+++ b/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/DynamicBoolTest.java
@@ -17,107 +17,143 @@
package androidx.wear.protolayout.expression;
import static androidx.wear.protolayout.expression.proto.DynamicProto.LogicalOpType.LOGICAL_OP_TYPE_AND;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.LogicalOpType.LOGICAL_OP_TYPE_EQUAL;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.LogicalOpType.LOGICAL_OP_TYPE_NOT_EQUAL;
import static androidx.wear.protolayout.expression.proto.DynamicProto.LogicalOpType.LOGICAL_OP_TYPE_OR;
+
import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertThrows;
import androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public final class DynamicBoolTest {
- private static final String STATE_KEY = "state-key";
+ private static final String STATE_KEY = "state-key";
- @Test
- public void constantBool() {
- DynamicBool falseBool = DynamicBool.constant(false);
- DynamicBool trueBool = DynamicBool.constant(true);
+ @Test
+ public void constantBool() {
+ DynamicBool falseBool = DynamicBool.constant(false);
+ DynamicBool trueBool = DynamicBool.constant(true);
- assertThat(falseBool.toDynamicBoolProto().getFixed().getValue()).isFalse();
- assertThat(trueBool.toDynamicBoolProto().getFixed().getValue()).isTrue();
- }
+ assertThat(falseBool.toDynamicBoolProto().getFixed().getValue()).isFalse();
+ assertThat(trueBool.toDynamicBoolProto().getFixed().getValue()).isTrue();
+ }
- @Test
- public void constantToString() {
- assertThat(DynamicBool.constant(true).toString()).isEqualTo("FixedBool{value=true}");
- }
+ @Test
+ public void constantToString() {
+ assertThat(DynamicBool.constant(true).toString()).isEqualTo("FixedBool{value=true}");
+ }
- @Test
- public void stateEntryValueBool() {
- DynamicBool stateBool = DynamicBool.fromState(STATE_KEY);
+ @Test
+ public void stateEntryValueBool() {
+ DynamicBool stateBool = DynamicBool.fromState(STATE_KEY);
- assertThat(stateBool.toDynamicBoolProto().getStateSource().getSourceKey()).isEqualTo(STATE_KEY);
- }
+ assertThat(stateBool.toDynamicBoolProto().getStateSource().getSourceKey())
+ .isEqualTo(STATE_KEY);
+ }
- @Test
- public void stateToString() {
- assertThat(DynamicBool.fromState("key").toString()).isEqualTo("StateBoolSource{sourceKey=key}");
- }
+ @Test
+ public void stateToString() {
+ assertThat(DynamicBool.fromState("key").toString())
+ .isEqualTo("StateBoolSource{sourceKey=key}");
+ }
- @Test
- public void andOpBool() {
- DynamicBool firstBool = DynamicBool.constant(false);
- DynamicBool secondBool = DynamicBool.constant(true);
+ @Test
+ public void andOpBool() {
+ DynamicBool firstBool = DynamicBool.constant(false);
+ DynamicBool secondBool = DynamicBool.constant(true);
- DynamicBool result = firstBool.and(secondBool);
- assertThat(result.toDynamicBoolProto().getLogicalOp().getOperationType())
- .isEqualTo(LOGICAL_OP_TYPE_AND);
- assertThat(result.toDynamicBoolProto().getLogicalOp().getInputLhs())
- .isEqualTo(firstBool.toDynamicBoolProto());
- assertThat(result.toDynamicBoolProto().getLogicalOp().getInputRhs())
- .isEqualTo(secondBool.toDynamicBoolProto());
- }
+ DynamicBool result = firstBool.and(secondBool);
+ assertThat(result.toDynamicBoolProto().getLogicalOp().getOperationType())
+ .isEqualTo(LOGICAL_OP_TYPE_AND);
+ assertThat(result.toDynamicBoolProto().getLogicalOp().getInputLhs())
+ .isEqualTo(firstBool.toDynamicBoolProto());
+ assertThat(result.toDynamicBoolProto().getLogicalOp().getInputRhs())
+ .isEqualTo(secondBool.toDynamicBoolProto());
+ }
- @Test
- public void orOpBool() {
- DynamicBool firstBool = DynamicBool.constant(false);
- DynamicBool secondBool = DynamicBool.constant(true);
+ @Test
+ public void orOpBool() {
+ DynamicBool firstBool = DynamicBool.constant(false);
+ DynamicBool secondBool = DynamicBool.constant(true);
- DynamicBool result = firstBool.or(secondBool);
- assertThat(result.toDynamicBoolProto().getLogicalOp().getOperationType())
- .isEqualTo(LOGICAL_OP_TYPE_OR);
- assertThat(result.toDynamicBoolProto().getLogicalOp().getInputLhs())
- .isEqualTo(firstBool.toDynamicBoolProto());
- assertThat(result.toDynamicBoolProto().getLogicalOp().getInputRhs())
- .isEqualTo(secondBool.toDynamicBoolProto());
- }
+ DynamicBool result = firstBool.or(secondBool);
+ assertThat(result.toDynamicBoolProto().getLogicalOp().getOperationType())
+ .isEqualTo(LOGICAL_OP_TYPE_OR);
+ assertThat(result.toDynamicBoolProto().getLogicalOp().getInputLhs())
+ .isEqualTo(firstBool.toDynamicBoolProto());
+ assertThat(result.toDynamicBoolProto().getLogicalOp().getInputRhs())
+ .isEqualTo(secondBool.toDynamicBoolProto());
+ }
- @Test
- public void logicalOpToString() {
- assertThat(DynamicBool.constant(true).and(DynamicBool.constant(false)).toString())
- .isEqualTo(
- "LogicalBoolOp{"
- + "inputLhs=FixedBool{value=true}, "
- + "inputRhs=FixedBool{value=false}, "
- + "operationType=1}");
- }
+ @Test
+ public void boolComparison_equalOp() {
+ DynamicBool firstBool = DynamicBool.constant(false);
+ DynamicBool secondBool = DynamicBool.constant(true);
- @Test
- public void negateOpBool() {
- DynamicBool firstBool = DynamicBool.constant(true);
+ DynamicBool result = firstBool.eq(secondBool);
+ assertThat(result.toDynamicBoolProto().getLogicalOp().getOperationType())
+ .isEqualTo(LOGICAL_OP_TYPE_EQUAL);
+ assertThat(result.toDynamicBoolProto().getLogicalOp().getInputLhs())
+ .isEqualTo(firstBool.toDynamicBoolProto());
+ assertThat(result.toDynamicBoolProto().getLogicalOp().getInputRhs())
+ .isEqualTo(secondBool.toDynamicBoolProto());
+ }
- assertThat(firstBool.negate().toDynamicBoolProto().getNotOp().getInput())
- .isEqualTo(firstBool.toDynamicBoolProto());
- }
+ @Test
+ public void boolComparison_notEqualOp() {
+ DynamicBool firstBool = DynamicBool.constant(false);
+ DynamicBool secondBool = DynamicBool.constant(true);
- @Test
- public void logicalToString() {
- assertThat(DynamicBool.constant(true).negate().toString())
- .isEqualTo("NotBoolOp{input=FixedBool{value=true}}");
- }
+ DynamicBool result = firstBool.ne(secondBool);
+ assertThat(result.toDynamicBoolProto().getLogicalOp().getOperationType())
+ .isEqualTo(LOGICAL_OP_TYPE_NOT_EQUAL);
+ assertThat(result.toDynamicBoolProto().getLogicalOp().getInputLhs())
+ .isEqualTo(firstBool.toDynamicBoolProto());
+ assertThat(result.toDynamicBoolProto().getLogicalOp().getInputRhs())
+ .isEqualTo(secondBool.toDynamicBoolProto());
+ }
- @Test
- public void validProto() {
- DynamicBool from = DynamicBool.constant(true);
- DynamicBool to = DynamicBool.fromByteArray(from.toDynamicBoolByteArray());
+ @Test
+ public void logicalOpToString() {
+ assertThat(DynamicBool.constant(true).and(DynamicBool.constant(false)).toString())
+ .isEqualTo(
+ "LogicalBoolOp{"
+ + "inputLhs=FixedBool{value=true}, "
+ + "inputRhs=FixedBool{value=false}, "
+ + "operationType=1}");
+ }
- assertThat(to.toDynamicBoolProto().getFixed().getValue()).isTrue();
- }
+ @Test
+ public void negateOpBool() {
+ DynamicBool firstBool = DynamicBool.constant(true);
- @Test
- public void invalidProto() {
- assertThrows(IllegalArgumentException.class, () -> DynamicBool.fromByteArray(new byte[] {1}));
- }
+ assertThat(firstBool.negate().toDynamicBoolProto().getNotOp().getInput())
+ .isEqualTo(firstBool.toDynamicBoolProto());
+ }
+
+ @Test
+ public void logicalToString() {
+ assertThat(DynamicBool.constant(true).negate().toString())
+ .isEqualTo("NotBoolOp{input=FixedBool{value=true}}");
+ }
+
+ @Test
+ public void validProto() {
+ DynamicBool from = DynamicBool.constant(true);
+ DynamicBool to = DynamicBool.fromByteArray(from.toDynamicBoolByteArray());
+
+ assertThat(to.toDynamicBoolProto().getFixed().getValue()).isTrue();
+ }
+
+ @Test
+ public void invalidProto() {
+ assertThrows(
+ IllegalArgumentException.class, () -> DynamicBool.fromByteArray(new byte[] {1}));
+ }
}
diff --git a/wear/protolayout/protolayout-proto/src/main/proto/action.proto b/wear/protolayout/protolayout-proto/src/main/proto/action.proto
index 72e7e95..589a31c 100644
--- a/wear/protolayout/protolayout-proto/src/main/proto/action.proto
+++ b/wear/protolayout/protolayout-proto/src/main/proto/action.proto
@@ -54,11 +54,11 @@
// A launch action to send an intent to an Android activity.
message AndroidActivity {
- // The package name to send the intent to, for example, "com.google.weather".
+ // The package name to send the intent to, for example, "com.example.weather".
string package_name = 1;
// The fully qualified class name (including the package) to send the intent
- // to, for example, "com.google.weather.WeatherOverviewActivity".
+ // to, for example, "com.example.weather.WeatherOverviewActivity".
string class_name = 2;
// The extras to be included in the intent.
diff --git a/wear/protolayout/protolayout-proto/src/main/proto/dynamic.proto b/wear/protolayout/protolayout-proto/src/main/proto/dynamic.proto
index b12923b..b0729ce 100644
--- a/wear/protolayout/protolayout-proto/src/main/proto/dynamic.proto
+++ b/wear/protolayout/protolayout-proto/src/main/proto/dynamic.proto
@@ -446,6 +446,12 @@
// Logical OR.
LOGICAL_OP_TYPE_OR = 2;
+
+ // Equal check
+ LOGICAL_OP_TYPE_EQUAL = 3;
+
+ // Not Equal check.
+ LOGICAL_OP_TYPE_NOT_EQUAL = 4;
}
// A logical boolean operator, implementing "boolean result = LHS <op> RHS",
diff --git a/wear/protolayout/protolayout-proto/src/main/proto/trigger.proto b/wear/protolayout/protolayout-proto/src/main/proto/trigger.proto
index f9cc1c5..24e5132 100644
--- a/wear/protolayout/protolayout-proto/src/main/proto/trigger.proto
+++ b/wear/protolayout/protolayout-proto/src/main/proto/trigger.proto
@@ -23,7 +23,7 @@
// condition is true initially, that will fire the trigger on load.
message OnConditionMetTrigger {
// Dynamic boolean used as trigger.
- androidx.wear.protolayout.expression.proto.DynamicBool trigger = 1;
+ androidx.wear.protolayout.expression.proto.DynamicBool condition = 1;
}
// The triggers that can be fired.
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 a50e95d..cc1b1a1 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
@@ -2446,7 +2446,7 @@
pipelineMaker
.get()
.addResolvedAnimatedImageWithBoolTrigger(
- avd, trigger, posId, conditionTrigger.getTrigger());
+ avd, trigger, posId, conditionTrigger.getCondition());
} else {
// Use default trigger if it's not set.
if (trigger == null
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java
index f8c0a3d..d89a564 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java
@@ -168,7 +168,8 @@
}
@Test
- public void buildPipeline_dpProp_animatable_animationsDisabled_hasStaticValue_assignsEndVal() {
+ public void
+ buildPipeline_dpProp_animatable_animationsDisabled_hasStaticValue_assignsEndValue() {
List<Float> results = new ArrayList<>();
float endValue = 10.0f;
DynamicFloat dynamicFloat = animatableFixedFloat(5.0f, endValue);
@@ -183,7 +184,7 @@
@Test
public void
- buildPipeline_degreesProp_animatable_animationsDisabled_hasStaticValue_assignsEndVal() {
+ buildPipeline_degreesProp_animatable_animationsDisabled_hasStaticValue_assignsEndValue() {
List<Float> results = new ArrayList<>();
float endValue = 10.0f;
DynamicFloat dynamicFloat = animatableFixedFloat(5.0f, endValue);
@@ -217,7 +218,7 @@
@Test
public void
- buildPipeline_colorProp_animatable_animationsDisabled_noStaticValueSet_assignsEndVal() {
+ buildPipeline_colorProp_animatable_animationsDisabled_noStaticValueSet_assignsEndValue() {
List<Integer> results = new ArrayList<>();
DynamicColor dynamicColor = animatableFixedColor(0, 1);
ColorProp colorProp = ColorProp.newBuilder().setDynamicValue(dynamicColor).build();
@@ -1041,7 +1042,7 @@
private static Trigger conditionTrigger(DynamicBool dynamicBool) {
return Trigger.newBuilder()
.setOnConditionMetTrigger(
- OnConditionMetTrigger.newBuilder().setTrigger(dynamicBool).build())
+ OnConditionMetTrigger.newBuilder().setCondition(dynamicBool).build())
.build();
}
@@ -1719,8 +1720,7 @@
Repeatable.newBuilder()
.setRepeatMode(
RepeatMode
- .REPEAT_MODE_REVERSE
- )
+ .REPEAT_MODE_REVERSE)
.setIterations(iterations)
.setForwardRepeatOverride(
alternateParameters)
@@ -1813,13 +1813,12 @@
ProtoLayoutDynamicDataPipeline pipeline =
enableAnimations
? new ProtoLayoutDynamicDataPipeline(
- /* sensorGateway= */ null,
+ /* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
new FixedQuotaManagerImpl(MAX_VALUE))
: new ProtoLayoutDynamicDataPipeline(
- /* sensorGateway= */ null,
- mStateStore);
+ /* sensorGateway= */ null, mStateStore);
shadowOf(getMainLooper()).idle();
pipeline.setFullyVisible(true);
@@ -1842,7 +1841,7 @@
AddToListCallback<Float> receiver =
new AddToListCallback<>(results, /* invalidList= */ null);
ProtoLayoutDynamicDataPipeline pipeline =
- new ProtoLayoutDynamicDataPipeline( /* sensorGateway= */ null, mStateStore);
+ new ProtoLayoutDynamicDataPipeline(/* sensorGateway= */ null, mStateStore);
shadowOf(getMainLooper()).idle();
pipeline.setFullyVisible(true);
@@ -1860,7 +1859,7 @@
AddToListCallback<Integer> receiver =
new AddToListCallback<>(results, /* invalidList= */ null);
ProtoLayoutDynamicDataPipeline pipeline =
- new ProtoLayoutDynamicDataPipeline( /* sensorGateway= */ null, mStateStore);
+ new ProtoLayoutDynamicDataPipeline(/* sensorGateway= */ null, mStateStore);
shadowOf(getMainLooper()).idle();
pipeline.setFullyVisible(true);
@@ -1878,7 +1877,7 @@
AddToListCallback<Float> receiver =
new AddToListCallback<>(results, /* invalidList= */ null);
ProtoLayoutDynamicDataPipeline pipeline =
- new ProtoLayoutDynamicDataPipeline( /* sensorGateway= */ null, mStateStore);
+ new ProtoLayoutDynamicDataPipeline(/* sensorGateway= */ null, mStateStore);
shadowOf(getMainLooper()).idle();
pipeline.setFullyVisible(true);
@@ -1896,7 +1895,7 @@
AddToListCallback<Float> receiver =
new AddToListCallback<>(results, /* invalidList= */ null);
ProtoLayoutDynamicDataPipeline pipeline =
- new ProtoLayoutDynamicDataPipeline( /* sensorGateway= */ null, mStateStore);
+ new ProtoLayoutDynamicDataPipeline(/* sensorGateway= */ null, mStateStore);
shadowOf(getMainLooper()).idle();
pipeline.setFullyVisible(true);
@@ -1914,7 +1913,7 @@
AddToListCallback<Integer> receiver =
new AddToListCallback<>(results, /* invalidList= */ null);
ProtoLayoutDynamicDataPipeline pipeline =
- new ProtoLayoutDynamicDataPipeline( /* sensorGateway= */ null, mStateStore);
+ new ProtoLayoutDynamicDataPipeline(/* sensorGateway= */ null, mStateStore);
shadowOf(getMainLooper()).idle();
pipeline.setFullyVisible(true);
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java
index 9e263bf..9a529c2 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java
@@ -4205,8 +4205,9 @@
private static FadeInTransition.Builder fadeIn(int delay) {
return FadeInTransition.newBuilder()
.setAnimationSpec(
- AnimationSpec.newBuilder().setAnimationParameters(
- AnimationParameters.newBuilder().setDelayMillis(delay)));
+ AnimationSpec.newBuilder()
+ .setAnimationParameters(
+ AnimationParameters.newBuilder().setDelayMillis(delay)));
}
private LayoutElement textFadeInSlideIn(String text) {
diff --git a/wear/protolayout/protolayout/api/current.txt b/wear/protolayout/protolayout/api/current.txt
index 208a7d4..506910e9 100644
--- a/wear/protolayout/protolayout/api/current.txt
+++ b/wear/protolayout/protolayout/api/current.txt
@@ -939,20 +939,6 @@
field public static final int IMAGE_FORMAT_UNDEFINED = 0; // 0x0
}
- public static final class ResourceBuilders.AndroidAnimatedImageResourceByResId {
- method public int getAnimatedImageFormat();
- method @DrawableRes public int getResourceId();
- method public androidx.wear.protolayout.TriggerBuilders.Trigger? getStartTrigger();
- }
-
- public static final class ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder {
- ctor public ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder();
- method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId build();
- method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder setAnimatedImageFormat(int);
- method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder setResourceId(@DrawableRes int);
- method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger);
- }
-
public static final class ResourceBuilders.AndroidImageResourceByResId {
method @DrawableRes public int getResourceId();
}
@@ -963,33 +949,15 @@
method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId.Builder setResourceId(@DrawableRes int);
}
- public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId {
- method public int getAnimatedImageFormat();
- method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getProgress();
- method @DrawableRes public int getResourceId();
- }
-
- public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder {
- ctor public ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder();
- method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId build();
- method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder setAnimatedImageFormat(int);
- method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat);
- method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder setResourceId(@DrawableRes int);
- }
-
public static final class ResourceBuilders.ImageResource {
- method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId? getAndroidAnimatedResourceByResId();
method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId? getAndroidResourceByResId();
- method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId? getAndroidSeekableAnimatedResourceByResId();
method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource? getInlineResource();
}
public static final class ResourceBuilders.ImageResource.Builder {
ctor public ResourceBuilders.ImageResource.Builder();
method public androidx.wear.protolayout.ResourceBuilders.ImageResource build();
- method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId);
method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId);
- method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId);
method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setInlineResource(androidx.wear.protolayout.ResourceBuilders.InlineImageResource);
}
@@ -1075,26 +1043,8 @@
}
public final class TriggerBuilders {
- method public static androidx.wear.protolayout.TriggerBuilders.OnConditionMetTrigger createOnConditionMetTrigger(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
- method public static androidx.wear.protolayout.TriggerBuilders.OnLoadTrigger createOnLoadTrigger();
- }
-
- public static final class TriggerBuilders.OnConditionMetTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
- method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool? getTrigger();
- }
-
- public static final class TriggerBuilders.OnConditionMetTrigger.Builder {
- ctor public TriggerBuilders.OnConditionMetTrigger.Builder();
- method public androidx.wear.protolayout.TriggerBuilders.OnConditionMetTrigger build();
- method public androidx.wear.protolayout.TriggerBuilders.OnConditionMetTrigger.Builder setTrigger(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
- }
-
- public static final class TriggerBuilders.OnLoadTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
- }
-
- public static final class TriggerBuilders.OnLoadTrigger.Builder {
- ctor public TriggerBuilders.OnLoadTrigger.Builder();
- method public androidx.wear.protolayout.TriggerBuilders.OnLoadTrigger build();
+ method public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnConditionMetTrigger(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
+ method public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnLoadTrigger();
}
public static interface TriggerBuilders.Trigger {
diff --git a/wear/protolayout/protolayout/api/public_plus_experimental_current.txt b/wear/protolayout/protolayout/api/public_plus_experimental_current.txt
index 4a1e57f..ce4acaa 100644
--- a/wear/protolayout/protolayout/api/public_plus_experimental_current.txt
+++ b/wear/protolayout/protolayout/api/public_plus_experimental_current.txt
@@ -1125,7 +1125,7 @@
field public static final int IMAGE_FORMAT_UNDEFINED = 0; // 0x0
}
- public static final class ResourceBuilders.AndroidAnimatedImageResourceByResId {
+ @androidx.wear.protolayout.expression.ProtoLayoutExperimental public static final class ResourceBuilders.AndroidAnimatedImageResourceByResId {
method public int getAnimatedImageFormat();
method @DrawableRes public int getResourceId();
method public androidx.wear.protolayout.TriggerBuilders.Trigger? getStartTrigger();
@@ -1149,7 +1149,7 @@
method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId.Builder setResourceId(@DrawableRes int);
}
- public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId {
+ @androidx.wear.protolayout.expression.ProtoLayoutExperimental public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId {
method public int getAnimatedImageFormat();
method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getProgress();
method @DrawableRes public int getResourceId();
@@ -1164,18 +1164,18 @@
}
public static final class ResourceBuilders.ImageResource {
- method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId? getAndroidAnimatedResourceByResId();
+ method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId? getAndroidAnimatedResourceByResId();
method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId? getAndroidResourceByResId();
- method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId? getAndroidSeekableAnimatedResourceByResId();
+ method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId? getAndroidSeekableAnimatedResourceByResId();
method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource? getInlineResource();
}
public static final class ResourceBuilders.ImageResource.Builder {
ctor public ResourceBuilders.ImageResource.Builder();
method public androidx.wear.protolayout.ResourceBuilders.ImageResource build();
- method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId);
+ method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId);
method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId);
- method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId);
+ method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId);
method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setInlineResource(androidx.wear.protolayout.ResourceBuilders.InlineImageResource);
}
@@ -1261,26 +1261,8 @@
}
public final class TriggerBuilders {
- method public static androidx.wear.protolayout.TriggerBuilders.OnConditionMetTrigger createOnConditionMetTrigger(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
- method public static androidx.wear.protolayout.TriggerBuilders.OnLoadTrigger createOnLoadTrigger();
- }
-
- public static final class TriggerBuilders.OnConditionMetTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
- method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool? getTrigger();
- }
-
- public static final class TriggerBuilders.OnConditionMetTrigger.Builder {
- ctor public TriggerBuilders.OnConditionMetTrigger.Builder();
- method public androidx.wear.protolayout.TriggerBuilders.OnConditionMetTrigger build();
- method public androidx.wear.protolayout.TriggerBuilders.OnConditionMetTrigger.Builder setTrigger(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
- }
-
- public static final class TriggerBuilders.OnLoadTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
- }
-
- public static final class TriggerBuilders.OnLoadTrigger.Builder {
- ctor public TriggerBuilders.OnLoadTrigger.Builder();
- method public androidx.wear.protolayout.TriggerBuilders.OnLoadTrigger build();
+ method public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnConditionMetTrigger(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
+ method public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnLoadTrigger();
}
public static interface TriggerBuilders.Trigger {
diff --git a/wear/protolayout/protolayout/api/restricted_current.txt b/wear/protolayout/protolayout/api/restricted_current.txt
index 208a7d4..506910e9 100644
--- a/wear/protolayout/protolayout/api/restricted_current.txt
+++ b/wear/protolayout/protolayout/api/restricted_current.txt
@@ -939,20 +939,6 @@
field public static final int IMAGE_FORMAT_UNDEFINED = 0; // 0x0
}
- public static final class ResourceBuilders.AndroidAnimatedImageResourceByResId {
- method public int getAnimatedImageFormat();
- method @DrawableRes public int getResourceId();
- method public androidx.wear.protolayout.TriggerBuilders.Trigger? getStartTrigger();
- }
-
- public static final class ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder {
- ctor public ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder();
- method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId build();
- method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder setAnimatedImageFormat(int);
- method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder setResourceId(@DrawableRes int);
- method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger);
- }
-
public static final class ResourceBuilders.AndroidImageResourceByResId {
method @DrawableRes public int getResourceId();
}
@@ -963,33 +949,15 @@
method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId.Builder setResourceId(@DrawableRes int);
}
- public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId {
- method public int getAnimatedImageFormat();
- method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getProgress();
- method @DrawableRes public int getResourceId();
- }
-
- public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder {
- ctor public ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder();
- method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId build();
- method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder setAnimatedImageFormat(int);
- method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat);
- method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder setResourceId(@DrawableRes int);
- }
-
public static final class ResourceBuilders.ImageResource {
- method public androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId? getAndroidAnimatedResourceByResId();
method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId? getAndroidResourceByResId();
- method public androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId? getAndroidSeekableAnimatedResourceByResId();
method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource? getInlineResource();
}
public static final class ResourceBuilders.ImageResource.Builder {
ctor public ResourceBuilders.ImageResource.Builder();
method public androidx.wear.protolayout.ResourceBuilders.ImageResource build();
- method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId);
method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId);
- method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId);
method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setInlineResource(androidx.wear.protolayout.ResourceBuilders.InlineImageResource);
}
@@ -1075,26 +1043,8 @@
}
public final class TriggerBuilders {
- method public static androidx.wear.protolayout.TriggerBuilders.OnConditionMetTrigger createOnConditionMetTrigger(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
- method public static androidx.wear.protolayout.TriggerBuilders.OnLoadTrigger createOnLoadTrigger();
- }
-
- public static final class TriggerBuilders.OnConditionMetTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
- method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool? getTrigger();
- }
-
- public static final class TriggerBuilders.OnConditionMetTrigger.Builder {
- ctor public TriggerBuilders.OnConditionMetTrigger.Builder();
- method public androidx.wear.protolayout.TriggerBuilders.OnConditionMetTrigger build();
- method public androidx.wear.protolayout.TriggerBuilders.OnConditionMetTrigger.Builder setTrigger(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
- }
-
- public static final class TriggerBuilders.OnLoadTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
- }
-
- public static final class TriggerBuilders.OnLoadTrigger.Builder {
- ctor public TriggerBuilders.OnLoadTrigger.Builder();
- method public androidx.wear.protolayout.TriggerBuilders.OnLoadTrigger build();
+ method public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnConditionMetTrigger(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
+ method public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnLoadTrigger();
}
public static interface TriggerBuilders.Trigger {
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
index f552d96..2fa280a 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
@@ -25,11 +25,13 @@
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.wear.protolayout.TriggerBuilders.Trigger;
import androidx.wear.protolayout.expression.DynamicBuilders;
import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat;
+import androidx.wear.protolayout.expression.ProtoLayoutExperimental;
import androidx.wear.protolayout.proto.ResourceProto;
import androidx.wear.protolayout.protobuf.ByteString;
@@ -333,6 +335,7 @@
*
* @since 1.2
*/
+ @ProtoLayoutExperimental
public static final class AndroidAnimatedImageResourceByResId {
private final ResourceProto.AndroidAnimatedImageResourceByResId mImpl;
@@ -463,6 +466,7 @@
*
* @since 1.2
*/
+ @ProtoLayoutExperimental
public static final class AndroidSeekableAnimatedImageResourceByResId {
private final ResourceProto.AndroidSeekableAnimatedImageResourceByResId mImpl;
@@ -649,6 +653,7 @@
* @since 1.2
*/
@Nullable
+ @ProtoLayoutExperimental
public AndroidAnimatedImageResourceByResId getAndroidAnimatedResourceByResId() {
if (mImpl.hasAndroidAnimatedResourceByResId()) {
return AndroidAnimatedImageResourceByResId.fromProto(
@@ -665,6 +670,7 @@
* @since 1.2
*/
@Nullable
+ @ProtoLayoutExperimental
public AndroidSeekableAnimatedImageResourceByResId
getAndroidSeekableAnimatedResourceByResId() {
if (mImpl.hasAndroidSeekableAnimatedResourceByResId()) {
@@ -696,6 +702,7 @@
@Override
@NonNull
+ @OptIn(markerClass = ProtoLayoutExperimental.class)
public String toString() {
return "ImageResource{"
+ "androidResourceByResId="
@@ -746,6 +753,7 @@
* @since 1.2
*/
@NonNull
+ @ProtoLayoutExperimental
public Builder setAndroidAnimatedResourceByResId(
@NonNull AndroidAnimatedImageResourceByResId androidAnimatedResourceByResId) {
mImpl.setAndroidAnimatedResourceByResId(androidAnimatedResourceByResId.toProto());
@@ -759,6 +767,7 @@
* @since 1.2
*/
@NonNull
+ @ProtoLayoutExperimental
public Builder setAndroidSeekableAnimatedResourceByResId(
@NonNull
AndroidSeekableAnimatedImageResourceByResId
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/StateBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/StateBuilders.java
index 8889ec0..0fb8101 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/StateBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/StateBuilders.java
@@ -149,9 +149,17 @@
return this;
}
+ private static final int MAX_STATE_SIZE = 30;
+
/** Builds an instance from accumulated values. */
@NonNull
public State build() {
+ if (mImpl.getIdToValueMap().size() > MAX_STATE_SIZE) {
+ throw new IllegalStateException(
+ String.format(
+ "State size is too large: %d. Maximum " + "allowed state size is %d.",
+ mImpl.getIdToValueMap().size(), MAX_STATE_SIZE));
+ }
return new State(mImpl.build(), mFingerprint);
}
}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java
index 4f544da..f5cb3c8 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java
@@ -31,17 +31,20 @@
public final class TriggerBuilders {
private TriggerBuilders() {}
- /** Shortcut for building an {@link OnLoadTrigger}. */
+ /** Creates a {@link Trigger} that fires immediately when the layout is loaded / reloaded.. */
@NonNull
- public static OnLoadTrigger createOnLoadTrigger() {
+ public static Trigger createOnLoadTrigger() {
return new OnLoadTrigger.Builder().build();
}
- /** Shortcut for building an {@link OnConditionMetTrigger}. */
+ /**
+ * Creates a {@link Trigger} that fires *every time* the condition switches from false to true.
+ * If the condition is true initially, that will fire the trigger on load.
+ */
@NonNull
- public static OnConditionMetTrigger createOnConditionMetTrigger(
+ public static Trigger createOnConditionMetTrigger(
@NonNull DynamicBool dynamicBool) {
- return new OnConditionMetTrigger.Builder().setTrigger(dynamicBool).build();
+ return new OnConditionMetTrigger.Builder().setCondition(dynamicBool).build();
}
/**
@@ -49,7 +52,7 @@
*
* @since 1.2
*/
- public static final class OnLoadTrigger implements Trigger {
+ static final class OnLoadTrigger implements Trigger {
private final TriggerProto.OnLoadTrigger mImpl;
@Nullable private final Fingerprint mFingerprint;
@@ -104,7 +107,7 @@
*
* @since 1.2
*/
- public static final class OnConditionMetTrigger implements Trigger {
+ static final class OnConditionMetTrigger implements Trigger {
private final TriggerProto.OnConditionMetTrigger mImpl;
@Nullable private final Fingerprint mFingerprint;
@@ -119,9 +122,9 @@
* @since 1.2
*/
@Nullable
- public DynamicBool getTrigger() {
- if (mImpl.hasTrigger()) {
- return DynamicBuilders.dynamicBoolFromProto(mImpl.getTrigger());
+ public DynamicBool getCondition() {
+ if (mImpl.hasCondition()) {
+ return DynamicBuilders.dynamicBoolFromProto(mImpl.getCondition());
} else {
return null;
}
@@ -165,8 +168,8 @@
* @since 1.2
*/
@NonNull
- public Builder setTrigger(@NonNull DynamicBool dynamicBool) {
- mImpl.setTrigger(dynamicBool.toDynamicBoolProto());
+ public Builder setCondition(@NonNull DynamicBool dynamicBool) {
+ mImpl.setCondition(dynamicBool.toDynamicBoolProto());
mFingerprint.recordPropertyUpdate(
1, checkNotNull(dynamicBool.getFingerprint()).aggregateValueAsInt());
return this;
diff --git a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/TriggerBuildersTest.java b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/TriggerBuildersTest.java
index cf36e4f..6230a66 100644
--- a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/TriggerBuildersTest.java
+++ b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/TriggerBuildersTest.java
@@ -29,7 +29,7 @@
@Test
public void onLoadTrigger() {
- TriggerBuilders.OnLoadTrigger onLoadTrigger = TriggerBuilders.createOnLoadTrigger();
+ TriggerBuilders.Trigger onLoadTrigger = TriggerBuilders.createOnLoadTrigger();
assertThat(onLoadTrigger.toTriggerProto().hasOnLoadTrigger()).isTrue();
}
@@ -39,12 +39,12 @@
public void onConditionTrigger() {
DynamicBuilders.DynamicBool condition = DynamicBuilders.DynamicBool.fromState("state");
- TriggerBuilders.OnConditionMetTrigger onConditionMetTrigger =
+ TriggerBuilders.Trigger onConditionMetTrigger =
TriggerBuilders.createOnConditionMetTrigger(
condition);
assertThat(
- onConditionMetTrigger.toTriggerProto().getOnConditionMetTrigger().getTrigger())
+ onConditionMetTrigger.toTriggerProto().getOnConditionMetTrigger().getCondition())
.isEqualTo(condition.toDynamicBoolProto());
}
}
diff --git a/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/renderer/TileRenderer.java b/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/renderer/TileRenderer.java
index 184aa81..3fb2267 100644
--- a/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/renderer/TileRenderer.java
+++ b/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/renderer/TileRenderer.java
@@ -202,15 +202,18 @@
public View inflate(@NonNull ViewGroup parent) {
String errorMessage =
"This method only works with the deprecated constructors that accept Layout and"
- + " Resources.";
+ + " Resources.";
try {
// Waiting for the result from future for backwards compatibility.
return inflateLayout(
- checkNotNull(mLayout, errorMessage),
- checkNotNull(mResources, errorMessage),
- parent).get(10, TimeUnit.SECONDS);
- } catch (ExecutionException | InterruptedException | CancellationException |
- TimeoutException e) {
+ checkNotNull(mLayout, errorMessage),
+ checkNotNull(mResources, errorMessage),
+ parent)
+ .get(10, TimeUnit.SECONDS);
+ } catch (ExecutionException
+ | InterruptedException
+ | CancellationException
+ | TimeoutException e) {
// Wrap checked exceptions to avoid changing the method signature.
throw new RuntimeException("Rendering tile has not successfully finished.", e);
}
@@ -219,13 +222,12 @@
/**
* Inflates a Tile into {@code parent}.
*
- * @param layout The portion of the Tile to render.
+ * @param layout The portion of the Tile to render.
* @param resources The resources for the Tile.
- * @param parent The view to attach the tile into.
+ * @param parent The view to attach the tile into.
* @return The future with the first child that was inflated. This may be null if the Layout is
- * empty or the top-level LayoutElement has no inner set, or the top-level LayoutElement
- * contains an
- * unsupported inner type.
+ * empty or the top-level LayoutElement has no inner set, or the top-level LayoutElement
+ * contains an unsupported inner type.
*/
@NonNull
public ListenableFuture<View> inflateAsync(
@@ -241,7 +243,6 @@
@NonNull ResourceProto.Resources resources,
@NonNull ViewGroup parent) {
ListenableFuture<Void> result = mInstance.renderAndAttach(layout, resources, parent);
- return FluentFuture.from(result)
- .transform(ignored -> parent.getChildAt(0), mUiExecutor);
+ return FluentFuture.from(result).transform(ignored -> parent.getChildAt(0), mUiExecutor);
}
}
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt
index 8a04d54..307c757 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt
@@ -118,8 +118,7 @@
/** Whether or not the watch face supports [renderWatchFaceToSurface]. */
public val isRenderWatchFaceToSurfaceSupported: Boolean
- @get:JvmName("isRenderWatchFaceToSurfaceSupported")
- get() = false
+ @get:JvmName("isRenderWatchFaceToSurfaceSupported") get() = false
/**
* Renders the [androidx.wear.watchface.ComplicationSlot] to a shared memory backed [Bitmap]
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt
index 17261be..93d8ddca 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt
@@ -88,10 +88,10 @@
}
/**
- * Intended for use by watch face editors, a RemoteWatchFaceViewHost allows the watch face to send
- * a [SurfaceControlViewHost.SurfacePackage] to the client, which the client can attach to a
- * [SurfaceView] with [SurfaceView.setChildSurfacePackage]. The client can request an updated
- * screen shot by calling [renderWatchFace].
+ * Intended for use by watch face editors, a RemoteWatchFaceViewHost allows the watch face to send a
+ * [SurfaceControlViewHost.SurfacePackage] to the client, which the client can attach to a
+ * [SurfaceView] with [SurfaceView.setChildSurfacePackage]. The client can request an updated screen
+ * shot by calling [renderWatchFace].
*/
public interface RemoteWatchFaceViewHost : AutoCloseable {
/**
@@ -161,8 +161,7 @@
/** Whether or not the watch face supports [RemoteWatchFaceViewHost]. */
public val isRemoteWatchFaceViewHostSupported: Boolean
- @get:JvmName("isRemoteWatchFaceViewHostSupported")
- get() = false
+ @get:JvmName("isRemoteWatchFaceViewHostSupported") get() = false
/**
* Constructs a [RemoteWatchFaceViewHost] whose [RemoteWatchFaceViewHost.surfacePackage] can be
@@ -180,7 +179,7 @@
* @param width The width of the view in pixels
* @param height The height of the view in pixels
* @return The [RemoteWatchFaceViewHost] or null if the client has already been closed or if the
- * watch face is not compatible.
+ * watch face is not compatible.
*/
@Throws(RemoteException::class)
@RequiresApi(Build.VERSION_CODES.R)
@@ -217,9 +216,7 @@
/**
* Renames this instance to [newInstanceId] (must be unique, usually this would be different
* from the old ID but that's not a requirement). Sets the current [UserStyle] represented as a
- * [UserStyleData> and clears any complication data. Setting the new UserStyle may have a side
- * effect of enabling or disabling complicationSlots, which will be visible via
- * [ComplicationSlotState.isEnabled].
+ * [UserStyleData> and clears any complication data. Setting the new UserStyle may have a side effect of enabling or disabling complicationSlots, which will be visible via [ComplicationSlotState.isEnabled].
*/
@Throws(RemoteException::class)
public fun updateWatchFaceInstance(newInstanceId: String, userStyle: UserStyleData)
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
index aba23ca..32d97b9 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
@@ -23,9 +23,9 @@
import android.os.IBinder
import android.os.RemoteException
import android.util.Log
-import androidx.core.util.Consumer
import androidx.annotation.Px
import androidx.annotation.RestrictTo
+import androidx.core.util.Consumer
import androidx.wear.watchface.Renderer
import androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy
import androidx.wear.watchface.complications.data.ComplicationData
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchUiState.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchUiState.kt
index 3fdeefa..9a1f229 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchUiState.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchUiState.kt
@@ -20,10 +20,7 @@
import androidx.annotation.IntDef
import androidx.annotation.RestrictTo
-/**
- * The InterruptionFilter.
- *
- */
+/** The InterruptionFilter. */
@RestrictTo(RestrictTo.Scope.LIBRARY)
@IntDef(
value =
diff --git a/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/HeadlessWatchFaceClientTest.kt b/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/HeadlessWatchFaceClientTest.kt
index 663fb70..11e4d9c 100644
--- a/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/HeadlessWatchFaceClientTest.kt
+++ b/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/HeadlessWatchFaceClientTest.kt
@@ -47,4 +47,4 @@
Assert.assertTrue(client.isRenderWatchFaceToSurfaceSupported)
}
-}
\ No newline at end of file
+}
diff --git a/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/ImmediateDataSourceService.kt b/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/ImmediateDataSourceService.kt
index 5194d6f..eb96145 100644
--- a/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/ImmediateDataSourceService.kt
+++ b/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/ImmediateDataSourceService.kt
@@ -48,12 +48,15 @@
ComplicationText.EMPTY
)
.build()
- ComplicationType.MONOCHROMATIC_IMAGE -> MonochromaticImageComplicationData.Builder(
- MonochromaticImage.Builder(
- Icon.createWithResource(this, R.drawable.heart)
- ).build(),
- ComplicationText.EMPTY
- ).build()
+ ComplicationType.MONOCHROMATIC_IMAGE ->
+ MonochromaticImageComplicationData.Builder(
+ MonochromaticImage.Builder(
+ Icon.createWithResource(this, R.drawable.heart)
+ )
+ .build(),
+ ComplicationText.EMPTY
+ )
+ .build()
else -> null
}
)
@@ -67,12 +70,13 @@
ComplicationType.LONG_TEXT ->
LongTextComplicationData.Builder(plainText("hello 123"), ComplicationText.EMPTY)
.build()
- ComplicationType.MONOCHROMATIC_IMAGE -> MonochromaticImageComplicationData.Builder(
- MonochromaticImage.Builder(
- Icon.createWithResource(this, R.drawable.heart)
- ).build(),
- ComplicationText.EMPTY
- ).build()
+ ComplicationType.MONOCHROMATIC_IMAGE ->
+ MonochromaticImageComplicationData.Builder(
+ MonochromaticImage.Builder(Icon.createWithResource(this, R.drawable.heart))
+ .build(),
+ ComplicationText.EMPTY
+ )
+ .build()
else -> null
}
}
diff --git a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
index daae481..8adf5fc 100644
--- a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
+++ b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
@@ -184,17 +184,13 @@
* android.support.wearable.complications.ACTION_COMPLICATION_UPDATE_REQUEST.
* - A ComplicationDataSourceService must include a `meta-data` tag with
* android.support.wearable.complications.SUPPORTED_TYPES in its manifest entry. The value of this
- * tag should be a comma separated list of types supported by the data source, from this table:
- * | Androidx class | Tag name |
- * |--------------------------------------|-------------------|
- * | [GoalProgressComplicationData] | GOAL_PROGRESS |
- * | [LongTextComplicationData] | LONG_TEXT |
- * | [MonochromaticImageComplicationData] | ICON |
- * | [PhotoImageComplicationData] | LARGE_IMAGE |
- * | [RangedValueComplicationData] | RANGED_TEXT |
- * | [ShortTextComplicationData] | SHORT_TEXT |
- * | [SmallImageComplicationData] | SMALL_IMAGE |
- * | [WeightedElementsComplicationData] | WEIGHTED_ELEMENTS |
+ * tag should be a comma separated list of types supported by the data source, from this table: |
+ * Androidx class | Tag name | |--------------------------------------|-------------------| |
+ * [GoalProgressComplicationData] | GOAL_PROGRESS | | [LongTextComplicationData] | LONG_TEXT | |
+ * [MonochromaticImageComplicationData] | ICON | | [PhotoImageComplicationData] | LARGE_IMAGE | |
+ * [RangedValueComplicationData] | RANGED_TEXT | | [ShortTextComplicationData] | SHORT_TEXT | |
+ * [SmallImageComplicationData] | SMALL_IMAGE | | [WeightedElementsComplicationData] |
+ * WEIGHTED_ELEMENTS |
*
* The order in which types are listed has no significance. In the case where a watch face supports
* multiple types in a single complication slot, the watch face will determine which types it
diff --git a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceUpdateRequester.kt b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceUpdateRequester.kt
index 6188e69..13b3f52 100644
--- a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceUpdateRequester.kt
+++ b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceUpdateRequester.kt
@@ -53,10 +53,7 @@
public fun requestUpdate(vararg complicationInstanceIds: Int)
public companion object {
- /**
- * The package of the service that accepts complication data source requests.
- *
- */
+ /** The package of the service that accepts complication data source requests. */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public const val UPDATE_REQUEST_RECEIVER_PACKAGE = "com.google.android.wearable.app"
diff --git a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt
index f16d472..720ff58 100644
--- a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt
+++ b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt
@@ -15,13 +15,13 @@
*/
package androidx.wear.watchface.complications.datasource
-import android.support.wearable.complications.ComplicationData as WireComplicationData
import android.content.Intent
import android.content.res.Resources
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
import android.os.RemoteException
+import android.support.wearable.complications.ComplicationData as WireComplicationData
import android.support.wearable.complications.IComplicationManager
import android.support.wearable.complications.IComplicationProvider
import android.util.Log
diff --git a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt
index 4bdd46d..949c589 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt
@@ -435,6 +435,7 @@
companion object {
private const val VERSION_NUMBER = 20
+
internal fun putIfNotNull(fields: Bundle, field: String, value: Parcelable?) {
if (value != null) {
fields.putParcelable(field, value)
@@ -1122,7 +1123,10 @@
(hasLongTitle() && longTitle?.expression != null) ||
(hasShortText() && shortText?.expression != null) ||
(hasShortTitle() && shortTitle?.expression != null) ||
- (hasContentDescription() && contentDescription?.expression != null)
+ (hasContentDescription() && contentDescription?.expression != null) ||
+ (placeholder?.hasExpression() ?: false) ||
+ (timelineEntries?.any { it.hasExpression() } ?: false) ||
+ (listEntries?.any { it.hasExpression() } ?: false)
/**
* Returns true if the complication data contains at least one text field with a value that may
@@ -1186,7 +1190,11 @@
(!isFieldValidForType(FIELD_LONG_TEXT, type) || longText == other.longText) &&
(!isFieldValidForType(FIELD_CONTENT_DESCRIPTION, type) ||
contentDescription == other.contentDescription) &&
- (!isFieldValidForType(FIELD_PLACEHOLDER_TYPE, type) || placeholder == other.placeholder)
+ (!isFieldValidForType(FIELD_PLACEHOLDER_TYPE, type) ||
+ placeholder == other.placeholder) &&
+ (!isFieldValidForType(FIELD_TIMELINE_ENTRIES, type) ||
+ timelineEntries == other.timelineEntries) &&
+ (!isFieldValidForType(EXP_FIELD_LIST_ENTRIES, type) || listEntries == other.listEntries)
/** Similar to [equals], but avoids comparing evaluated fields (if expressions exist). */
infix fun equalsUnevaluated(other: ComplicationData): Boolean =
@@ -1208,18 +1216,35 @@
(!isFieldValidForType(FIELD_LONG_TEXT, type) ||
longText equalsUnevaluated other.longText) &&
(!isFieldValidForType(FIELD_CONTENT_DESCRIPTION, type) ||
- contentDescription.equalsUnevaluated(other.contentDescription)) &&
+ contentDescription equalsUnevaluated other.contentDescription) &&
(!isFieldValidForType(FIELD_PLACEHOLDER_TYPE, type) ||
- ((placeholder == null && other.placeholder == null) ||
- ((placeholder != null && other.placeholder != null) &&
- placeholder!! equalsUnevaluated other.placeholder!!)))
+ placeholder equalsUnevaluated other.placeholder) &&
+ (!isFieldValidForType(FIELD_TIMELINE_ENTRIES, type) ||
+ timelineEntries equalsUnevaluated other.timelineEntries) &&
+ (!isFieldValidForType(EXP_FIELD_LIST_ENTRIES, type) ||
+ listEntries equalsUnevaluated other.listEntries)
+
+ private infix fun ComplicationData?.equalsUnevaluated(other: ComplicationData?): Boolean {
+ if (this == null && other == null) return true
+ if (this == null || other == null) return false
+ // Both are non-null.
+ return this equalsUnevaluated other
+ }
+
+ private infix fun List<ComplicationData>?.equalsUnevaluated(
+ other: List<ComplicationData>?
+ ): Boolean {
+ if (this == null && other == null) return true
+ if (this == null || other == null) return false
+ return this.size == other.size && this.zip(other).all { (a, b) -> a equalsUnevaluated b }
+ }
private infix fun ComplicationText?.equalsUnevaluated(other: ComplicationText?): Boolean {
if (this == null && other == null) return true
if (this == null || other == null) return false
// Both are non-null.
- if (expression == null) return equals(other)
- return expression?.toDynamicStringByteArray() contentEquals
+ if (this.expression == null) return equals(other)
+ return this.expression?.toDynamicStringByteArray() contentEquals
other.expression?.toDynamicStringByteArray()
}
@@ -1232,10 +1257,6 @@
timelineStartEpochSecond == other.timelineStartEpochSecond) &&
(!isFieldValidForType(FIELD_TIMELINE_END_TIME, type) ||
timelineEndEpochSecond == other.timelineEndEpochSecond) &&
- (!isFieldValidForType(FIELD_TIMELINE_ENTRIES, type) ||
- timelineEntries == other.timelineEntries) &&
- (!isFieldValidForType(EXP_FIELD_LIST_ENTRIES, type) ||
- listEntries == other.listEntries) &&
(!isFieldValidForType(FIELD_DATA_SOURCE, type) || dataSource == other.dataSource) &&
(!isFieldValidForType(FIELD_VALUE_TYPE, type) ||
rangedValueType == other.rangedValueType) &&
@@ -1815,13 +1836,13 @@
*
* Returns this Builder to allow chaining.
*/
- fun setListEntryCollection(timelineEntries: Collection<ComplicationData>?) = apply {
- if (timelineEntries == null) {
+ fun setListEntryCollection(listEntries: Collection<ComplicationData>?) = apply {
+ if (listEntries == null) {
fields.remove(EXP_FIELD_LIST_ENTRIES)
} else {
fields.putParcelableArray(
EXP_FIELD_LIST_ENTRIES,
- timelineEntries
+ listEntries
.map { data ->
data.fields.putInt(EXP_FIELD_LIST_ENTRY_TYPE, data.type)
data.fields
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt
index 89fbc08..ddf43de 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt
@@ -16,10 +16,10 @@
package androidx.wear.watchface.complications.data
-import android.support.wearable.complications.ComplicationData as WireComplicationData
-import android.support.wearable.complications.ComplicationText as WireComplicationText
import android.icu.util.ULocale
-import android.support.wearable.complications.ComplicationData
+import android.support.wearable.complications.ComplicationData as WireComplicationData
+import android.support.wearable.complications.ComplicationData.Companion.TYPE_NO_DATA
+import android.support.wearable.complications.ComplicationText as WireComplicationText
import androidx.annotation.MainThread
import androidx.annotation.RestrictTo
import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
@@ -41,17 +41,19 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emitAll
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.flow.updateAndGet
import kotlinx.coroutines.invoke
import kotlinx.coroutines.launch
/**
* Evaluates a [WireComplicationData] with
* [androidx.wear.protolayout.expression.DynamicBuilders.DynamicType] within its fields.
- *
- * Due to [WireComplicationData]'s shallow copy strategy the input is modified in-place.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
class ComplicationDataExpressionEvaluator(
@@ -65,27 +67,101 @@
*
* The expression is evaluated _separately_ on each flow collection.
*/
- fun evaluate(unevaluatedData: WireComplicationData) =
- flow<WireComplicationData> {
- val state: MutableStateFlow<State> = unevaluatedData.buildState()
- state.value.use {
- val evaluatedData: Flow<WireComplicationData> =
- state
- .mapNotNull {
- when {
- // Emitting INVALID_DATA if there's an invalid receiver.
- it.invalidReceivers.isNotEmpty() -> INVALID_DATA
- // Emitting the data if all pending receivers are done and all
- // pre-updates are satisfied.
- it.pendingReceivers.isEmpty() -> it.data
- // Skipping states that are not ready for be emitted.
- else -> null
- }
- }
- .distinctUntilChanged()
- emitAll(evaluatedData)
+ fun evaluate(unevaluatedData: WireComplicationData): Flow<WireComplicationData> =
+ evaluateTopLevelFields(unevaluatedData)
+ // Combining with fields that are made of WireComplicationData.
+ .combineWithDataList(unevaluatedData.timelineEntries) { entries ->
+ // Timeline entries are set on the built WireComplicationData.
+ WireComplicationData.Builder(
+ this@combineWithDataList.build().apply { setTimelineEntryCollection(entries) }
+ )
}
+ .combineWithDataList(unevaluatedData.listEntries) { setListEntryCollection(it) }
+ // Must be last, as it overwrites INVALID_DATA.
+ .combineWithEvaluatedPlaceholder(unevaluatedData.placeholder)
+ .distinctUntilChanged()
+
+ /** Evaluates "local" fields, excluding fields of type WireComplicationData. */
+ private fun evaluateTopLevelFields(
+ unevaluatedData: WireComplicationData
+ ): Flow<WireComplicationData> = flow {
+ val state: MutableStateFlow<State> = unevaluatedData.buildState()
+ state.value.use {
+ val evaluatedData: Flow<WireComplicationData> =
+ state.mapNotNull {
+ when {
+ // Emitting INVALID_DATA if there's an invalid receiver.
+ it.invalidReceivers.isNotEmpty() -> INVALID_DATA
+ // Emitting the data if all pending receivers are done and all
+ // pre-updates are satisfied.
+ it.pendingReceivers.isEmpty() -> it.data
+ // Skipping states that are not ready for be emitted.
+ else -> null
+ }
+ }
+ emitAll(evaluatedData)
}
+ }
+
+ /**
+ * Combines the receiver with the evaluated version of the provided list.
+ *
+ * If the receiver [Flow] emits [INVALID_DATA] or the input list is null or empty, this does not
+ * mutate the flow and does not wait for the entries to finish evaluating.
+ *
+ * If even one [WireComplicationData] within the provided list is evaluated to [INVALID_DATA],
+ * the output [Flow] becomes [INVALID_DATA] (the receiver [Flow] is ignored).
+ */
+ private fun Flow<WireComplicationData>.combineWithDataList(
+ unevaluatedEntries: List<WireComplicationData>?,
+ setter:
+ WireComplicationData.Builder.(
+ List<WireComplicationData>
+ ) -> WireComplicationData.Builder,
+ ): Flow<WireComplicationData> {
+ if (unevaluatedEntries.isNullOrEmpty()) return this
+ val evaluatedEntriesFlow: Flow<List<WireComplicationData>> =
+ combine(unevaluatedEntries.map { evaluate(it) })
+
+ return this.combine(evaluatedEntriesFlow).map {
+ (data: WireComplicationData, evaluatedEntries: List<WireComplicationData>?) ->
+ // Not mutating if invalid.
+ if (data === INVALID_DATA) return@map data
+ // An entry is invalid, emitting invalid.
+ if (evaluatedEntries.any { it === INVALID_DATA }) return@map INVALID_DATA
+ // All is well, mutating the input.
+ return@map WireComplicationData.Builder(data).setter(evaluatedEntries).build()
+ }
+ }
+
+ /**
+ * Same as [combineWithDataList], but sets the evaluated placeholder ONLY when the receiver
+ * [Flow] emits [TYPE_NO_DATA], or [keepExpression] is true, otherwise clears it and does not
+ * wait for the placeholder to finish evaluating.
+ *
+ * If the placeholder is not required (per the above paragraph), this doesn't wait for it.
+ */
+ private fun Flow<WireComplicationData>.combineWithEvaluatedPlaceholder(
+ unevaluatedPlaceholder: WireComplicationData?
+ ): Flow<WireComplicationData> {
+ if (unevaluatedPlaceholder == null) return this
+ val evaluatedPlaceholderFlow: Flow<WireComplicationData> = evaluate(unevaluatedPlaceholder)
+
+ return this.combine(evaluatedPlaceholderFlow).map {
+ (data: WireComplicationData, evaluatedPlaceholder: WireComplicationData?) ->
+ if (!keepExpression && data.type != TYPE_NO_DATA) {
+ // Clearing the placeholder when data is not TYPE_NO_DATA (it was meant as an
+ // expression fallback).
+ return@map WireComplicationData.Builder(data).setPlaceholder(null).build()
+ }
+ // Placeholder required but invalid, emitting invalid.
+ if (evaluatedPlaceholder === INVALID_DATA) return@map INVALID_DATA
+ // All is well, mutating the input.
+ return@map WireComplicationData.Builder(data)
+ .setPlaceholder(evaluatedPlaceholder)
+ .build()
+ }
+ }
private suspend fun WireComplicationData.buildState() =
MutableStateFlow(State(this)).apply {
@@ -177,7 +253,7 @@
* [ComplicationEvaluationResultReceiver] that are evaluating it.
*/
private inner class State(
- val data: ComplicationData,
+ val data: WireComplicationData,
val pendingReceivers: Set<ComplicationEvaluationResultReceiver<out Any>> = setOf(),
val invalidReceivers: Set<ComplicationEvaluationResultReceiver<out Any>> = setOf(),
val completeReceivers: Set<ComplicationEvaluationResultReceiver<out Any>> = setOf(),
@@ -317,3 +393,35 @@
runnable.run()
}
}
+
+/** Replacement of [kotlinx.coroutines.flow.combine], which doesn't seem to work. */
+internal fun <T> combine(flows: List<Flow<T>>): Flow<List<T>> = flow {
+ data class ValueExists(val value: T?, val exists: Boolean)
+ val latest = MutableStateFlow(List(flows.size) { ValueExists(null, false) })
+ @Suppress("UNCHECKED_CAST") // Flow<List<T?>> -> Flow<List<T>> safe after filtering exists.
+ emitAll(
+ flows
+ .mapIndexed { i, flow -> flow.map { i to it } } // List<Flow<Int, T>> (indexed flows)
+ .merge() // Flow<Int, T>
+ .map { (i, value) ->
+ // Updating latest and returning the current latest.
+ latest.updateAndGet {
+ val newLatest = it.toMutableList()
+ newLatest[i] = ValueExists(value, true)
+ newLatest
+ }
+ } // Flow<List<ValueExists>>
+ // Filtering emissions until we have all values.
+ .filter { values -> values.all { it.exists } }
+ // Flow<List<T>> + defensive copy.
+ .map { values -> values.map { it.value } } as Flow<List<T>>
+ )
+}
+
+/**
+ * Another replacement of [kotlinx.coroutines.flow.combine] which is similar to
+ * `combine(List<Flow<T>>)` but allows different types for each flow.
+ */
+@Suppress("UNCHECKED_CAST")
+internal fun <T1, T2> Flow<T1>.combine(other: Flow<T2>): Flow<Pair<T1, T2>> =
+ combine(listOf(this as Flow<*>, other as Flow<*>)).map { (a, b) -> (a as T1) to (b as T2) }
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
index e34d53c..1215a97 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
@@ -907,6 +907,20 @@
displayPolicy = displayPolicy,
fallback = fallback,
) {
+
+ init {
+ require(min <= max) { "min must be lower than or equal to max" }
+ require(value == PLACEHOLDER || value in min..max) { "value must be between min and max" }
+ require(max != Float.MAX_VALUE) { "Float.MAX_VALUE is reserved and can't be used for max" }
+ require(monochromaticImage != null || smallImage != null || text != null || title != null) {
+ "At least one of monochromaticImage, smallImage, text or title must be set"
+ }
+ if (valueType == TYPE_PERCENTAGE) {
+ require(min == 0f)
+ require(max == 100f)
+ }
+ }
+
/**
* The [DynamicFloat] optionally set by the data source. If present the system will dynamically
* evaluate this and store the result in [value]. Watch faces can typically ignore this field.
@@ -988,16 +1002,6 @@
@RangedValueType private var valueType: Int = TYPE_UNDEFINED
- init {
- require(min <= max) { "min must be lower than or equal to max" }
- require(value == PLACEHOLDER || value in min..max) {
- "value must be between min and max"
- }
- require(max != Float.MAX_VALUE) {
- "Float.MAX_VALUE is reserved and can't be used for max"
- }
- }
-
/** Sets optional pending intent to be invoked when the complication is tapped. */
public fun setTapAction(tapAction: PendingIntent?): Builder = apply {
this.tapAction = tapAction
@@ -1043,17 +1047,8 @@
}
/** Builds the [RangedValueComplicationData]. */
- public override fun build(): RangedValueComplicationData {
- require(
- monochromaticImage != null || smallImage != null || text != null || title != null
- ) {
- "At least one of monochromaticImage, smallImage, text or title must be set"
- }
- if (valueType == TYPE_PERCENTAGE) {
- require(min == 0f)
- require(max == 100f)
- }
- return RangedValueComplicationData(
+ public override fun build() =
+ RangedValueComplicationData(
value,
valueExpression,
min,
@@ -1073,7 +1068,6 @@
displayPolicy,
fallback,
)
- }
}
override fun fillWireComplicationDataBuilder(builder: WireComplicationDataBuilder) {
@@ -1273,6 +1267,16 @@
displayPolicy = displayPolicy,
fallback = fallback,
) {
+
+ init {
+ require(targetValue != Float.MAX_VALUE) {
+ "Float.MAX_VALUE is reserved and can't be used for target"
+ }
+ require(monochromaticImage != null || smallImage != null || text != null || title != null) {
+ "At least one of monochromaticImage, smallImage, text or title must be set"
+ }
+ }
+
/**
* The [DynamicFloat] optionally set by the data source. If present the system will dynamically
* evaluate this and store the result in [value]. Watch faces can typically ignore this field.
@@ -1345,12 +1349,6 @@
private var text: ComplicationText? = null
private var colorRamp: ColorRamp? = null
- init {
- require(targetValue != Float.MAX_VALUE) {
- "Float.MAX_VALUE is reserved and can't be used for target"
- }
- }
-
/** Sets optional pending intent to be invoked when the complication is tapped. */
public fun setTapAction(tapAction: PendingIntent?): Builder = apply {
this.tapAction = tapAction
@@ -1387,13 +1385,8 @@
}
/** Builds the [GoalProgressComplicationData]. */
- public override fun build(): GoalProgressComplicationData {
- require(
- monochromaticImage != null || smallImage != null || text != null || title != null
- ) {
- "At least one of monochromaticImage, smallImage, text or title must be set"
- }
- return GoalProgressComplicationData(
+ public override fun build() =
+ GoalProgressComplicationData(
value,
valueExpression,
targetValue,
@@ -1411,7 +1404,6 @@
displayPolicy,
fallback = fallback,
)
- }
}
override fun fillWireComplicationDataBuilder(builder: WireComplicationDataBuilder) {
@@ -1585,6 +1577,12 @@
displayPolicy = displayPolicy,
fallback = fallback,
) {
+
+ init {
+ require(monochromaticImage != null || smallImage != null || text != null || title != null) {
+ "At least one of monochromaticImage, smallImage, text or title must be set"
+ }
+ }
/**
* Describes a single value within a [WeightedElementsComplicationData].
*
@@ -1710,13 +1708,8 @@
public fun setText(text: ComplicationText?): Builder = apply { this.text = text }
/** Builds the [GoalProgressComplicationData]. */
- public override fun build(): WeightedElementsComplicationData {
- require(
- monochromaticImage != null || smallImage != null || text != null || title != null
- ) {
- "At least one of monochromaticImage, smallImage, text or title must be set"
- }
- return WeightedElementsComplicationData(
+ public override fun build() =
+ WeightedElementsComplicationData(
elements,
elementBackgroundColor,
monochromaticImage,
@@ -1732,7 +1725,6 @@
displayPolicy,
fallback,
)
- }
}
override fun fillWireComplicationDataBuilder(builder: WireComplicationDataBuilder) {
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
index de0c253..9d2d48f 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
@@ -516,6 +516,7 @@
override fun isPlaceholder(): Boolean = delegate.isPlaceholder()
override fun isAlwaysEmpty() = delegate.isAlwaysEmpty
+
override fun getTimeDependentText(): TimeDependentText = delegate.timeDependentText
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) override fun toWireComplicationText() = delegate
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Type.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Type.kt
index 2ed2aba..86183f0 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Type.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Type.kt
@@ -48,7 +48,6 @@
* Converts this value to the integer value used for serialization.
*
* This is only needed internally to convert to the underlying communication protocol.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY) public fun toWireComplicationType(): Int = wireType
@@ -59,7 +58,6 @@
* Converts the integer value used for serialization into a [ComplicationType].
*
* This is only needed internally to convert to the underlying communication protocol.
- *
*/
@OptIn(ComplicationExperimental::class)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -89,7 +87,6 @@
* This is only needed internally to convert to the underlying communication protocol.
*
* Needed to access this conveniently in Java.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@JvmStatic
@@ -102,7 +99,6 @@
* This is only needed internally to convert to the underlying communication protocol.
*
* Needed to access this conveniently in Java.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@JvmStatic
@@ -112,7 +108,6 @@
/**
* Converts an array of integer values used for serialization into the corresponding list of
* [ComplicationType].
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@JvmStatic
@@ -126,7 +121,6 @@
* types.
*
* This is only needed internally to convert to the underlying communication protocol.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public fun Collection<ComplicationType>.toWireTypes(): IntArray =
@@ -137,7 +131,6 @@
* [ComplicationType] to .
*
* This is only needed internally to convert to the underlying communication protocol.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public fun IntArray.toApiComplicationTypes(): Array<ComplicationType> =
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/IconKt.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/IconKt.kt
index b55837b..88d4d32 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/IconKt.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/IconKt.kt
@@ -24,10 +24,7 @@
import androidx.annotation.RestrictTo
import java.util.Objects
-/**
- * Returns true if the [Icon]s are equal.
- *
- */
+/** Returns true if the [Icon]s are equal. */
infix fun Icon?.iconEquals(other: Icon?): Boolean =
this === other ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
@@ -36,10 +33,7 @@
this == other
}
-/**
- * Creates a hash code for the [Icon].
- *
- */
+/** Creates a hash code for the [Icon]. */
fun Icon.iconHashCode(): Int =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
IconP.hashCode(this)
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/TraceEvent.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/TraceEvent.kt
index 7239b5e..18e1f0f 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/TraceEvent.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/utility/TraceEvent.kt
@@ -29,7 +29,6 @@
/**
* Wrapper around [Trace.beginSection] and [Trace.endSection] which helps reduce boilerplate by
* taking advantage of RAII like [Closeable] in a try block.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class TraceEvent(traceName: String) : Closeable {
@@ -45,7 +44,6 @@
/**
* Wrapper around [Trace.beginAsyncSection] which helps reduce boilerplate by taking advantage of
* RAII like [Trace.endAsyncSection] in a try block, and by dealing with API version support.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class AsyncTraceEvent(private val traceName: String) : Closeable {
@@ -86,10 +84,7 @@
}
}
-/**
- * Wrapper around [CoroutineScope.launch] with an async trace event.
- *
- */
+/** Wrapper around [CoroutineScope.launch] with an async trace event. */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public fun CoroutineScope.launchWithTracing(
traceEventName: String,
diff --git a/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataEqualityTest.kt b/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataEqualityTest.kt
index 29611f1..7f7a002 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataEqualityTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataEqualityTest.kt
@@ -21,6 +21,8 @@
import android.content.Intent
import android.graphics.drawable.Icon
import android.os.Build
+import android.support.wearable.complications.ComplicationData.Companion.TYPE_NO_DATA
+import android.support.wearable.complications.ComplicationData.Companion.TYPE_SHORT_TEXT
import android.support.wearable.complications.ComplicationText.plainText
import android.util.Log
import androidx.test.core.app.ApplicationProvider
@@ -67,12 +69,12 @@
val setterTwo: ComplicationData.Builder.() -> Unit,
) {
PERSISTENCE_POLICY(
- { setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED) },
{ setPersistencePolicy(ComplicationPersistencePolicies.DO_NOT_PERSIST) },
+ { setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED) },
),
DISPLAY_POLICY(
- { setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY) },
{ setDisplayPolicy(ComplicationDisplayPolicies.DO_NOT_SHOW_WHEN_DEVICE_LOCKED) },
+ { setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY) },
),
START_DATE_TIME_MILLIS(
{ setStartDateTimeMillis(1) },
@@ -181,20 +183,8 @@
{ setTapActionLostDueToSerialization(false) },
),
PLACEHOLDER(
- {
- setPlaceholder(
- ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
- .setShortText(plainText("1"))
- .build()
- )
- },
- {
- setPlaceholder(
- ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
- .setShortText(plainText("2"))
- .build()
- )
- },
+ { setPlaceholder(staticData("1")) },
+ { setPlaceholder(staticData("2")) },
),
DATA_SOURCE(
{ setDataSource(ComponentName.createRelative("", "1")) },
@@ -221,24 +211,8 @@
{ setColorRampIsSmoothShaded(false) },
),
LIST_ENTRY_COLLECTION(
- {
- setListEntryCollection(
- listOf(
- ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
- .setShortText(plainText("1"))
- .build()
- )
- )
- },
- {
- setListEntryCollection(
- listOf(
- ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
- .setShortText(plainText("2"))
- .build()
- )
- )
- },
+ { setListEntryCollection(listOf(staticData("1"))) },
+ { setListEntryCollection(listOf(staticData("2"))) },
),
ELEMENT_WEIGHTS(
{ setElementWeights(floatArrayOf(1f, 2f)) },
@@ -261,24 +235,12 @@
{ build().apply { timelineEndEpochSecond = 200 } },
),
TIMELINE_ENTRIES(
- {
- build().apply {
- setTimelineEntryCollection(
- listOf(ComplicationData.Builder(this).setRangedValue(1f).build())
- )
- }
- },
- {
- build().apply {
- setTimelineEntryCollection(
- listOf(ComplicationData.Builder(this).setRangedValue(2f).build())
- )
- }
- },
+ { build().apply { setTimelineEntryCollection(listOf(staticData("1"))) } },
+ { build().apply { setTimelineEntryCollection(listOf(staticData("2"))) } },
),
;
- private val base = ComplicationData.TYPE_NO_DATA
+ val base = ComplicationData.Builder(TYPE_NO_DATA).build()
/** Builds a [ComplicationData] with the first variation. */
fun buildOne() = ComplicationData.Builder(base).apply { setterOne(this) }.build()
@@ -299,6 +261,10 @@
.withMessage("${scenario.name} does not equal another")
.that(scenario.buildOne())
.isNotEqualTo(scenario.buildTwo())
+ expect
+ .withMessage("${scenario.name} does not equal unset")
+ .that(scenario.buildOne())
+ .isNotEqualTo(scenario.base)
}
}
@@ -314,6 +280,10 @@
.withMessage("${scenario.name} does not equal another")
.that(scenario.buildOne().hashCode())
.isNotEqualTo(scenario.buildTwo().hashCode())
+ expect
+ .withMessage("${scenario.name} does not equal unset")
+ .that(scenario.buildOne().hashCode())
+ .isNotEqualTo(scenario.base.hashCode())
}
}
@@ -346,179 +316,77 @@
.setRangedValueExpression(DynamicFloat.constant(3.4f))
},
),
-
- // Not ignored without an expression.
RANGED_VALUE_NO_EXPRESSION(
{ setRangedValue(1f) },
{ setRangedValue(2f) },
),
SHORT_TITLE_EXPRESSION(
- {
- setShortTitle(
- ComplicationText(
- Random.nextInt().toString(), // Ignored when there's an expression.
- DynamicString.constant("1")
- )
- )
- },
- {
- setShortTitle(
- ComplicationText(
- Random.nextInt().toString(), // Ignored when there's an expression.
- DynamicString.constant("2")
- )
- )
- },
+ { setShortTitle(expressionText("1")) },
+ { setShortTitle(expressionText("2")) },
),
-
- // Not ignored without an expression.
SHORT_TITLE_NO_EXPRESSION(
{ setShortTitle(plainText("1")) },
{ setShortTitle(plainText("2")) },
),
SHORT_TEXT_EXPRESSION(
- {
- setShortText(
- ComplicationText(
- Random.nextInt().toString(), // Ignored when there's an expression.
- DynamicString.constant("1")
- )
- )
- },
- {
- setShortText(
- ComplicationText(
- Random.nextInt().toString(), // Ignored when there's an expression.
- DynamicString.constant("2")
- )
- )
- },
+ { setShortText(expressionText("1")) },
+ { setShortText(expressionText("2")) },
),
-
- // Not ignored without an expression.
SHORT_TEXT_NO_EXPRESSION(
{ setShortText(plainText("1")) },
{ setShortText(plainText("2")) },
),
LONG_TITLE_EXPRESSION(
- {
- setLongTitle(
- ComplicationText(
- Random.nextInt().toString(), // Ignored when there's an expression.
- DynamicString.constant("1")
- )
- )
- },
- {
- setLongTitle(
- ComplicationText(
- Random.nextInt().toString(), // Ignored when there's an expression.
- DynamicString.constant("2")
- )
- )
- },
+ { setLongTitle(expressionText("1")) },
+ { setLongTitle(expressionText("2")) },
),
-
- // Not ignored without an expression.
LONG_TITLE_NO_EXPRESSION(
{ setLongTitle(plainText("1")) },
{ setLongTitle(plainText("2")) },
),
LONG_TEXT_EXPRESSION(
- {
- setLongText(
- ComplicationText(
- Random.nextInt().toString(), // Ignored when there's an expression.
- DynamicString.constant("1")
- )
- )
- },
- {
- setLongText(
- ComplicationText(
- Random.nextInt().toString(), // Ignored when there's an expression.
- DynamicString.constant("2")
- )
- )
- },
+ { setLongText(expressionText("1")) },
+ { setLongText(expressionText("2")) },
),
-
- // Not ignored without an expression.
LONG_TEXT_NO_EXPRESSION(
{ setLongText(plainText("1")) },
{ setLongText(plainText("2")) },
),
CONTENT_DESCRIPTION_EXPRESSION(
- {
- setContentDescription(
- ComplicationText(
- Random.nextInt().toString(), // Ignored when there's an expression.
- DynamicString.constant("1")
- )
- )
- },
- {
- setContentDescription(
- ComplicationText(
- Random.nextInt().toString(), // Ignored when there's an expression.
- DynamicString.constant("2")
- )
- )
- },
+ { setContentDescription(expressionText("1")) },
+ { setContentDescription(expressionText("2")) },
),
-
- // Not ignored without an expression.
CONTENT_DESCRIPTION_NO_EXPRESSION(
{ setContentDescription(plainText("1")) },
{ setContentDescription(plainText("2")) },
),
PLACEHOLDER_EXPRESSION(
- {
- setPlaceholder(
- ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
- .setShortText(
- ComplicationText(
- Random.nextInt().toString(), // Ignored when there's an expression.
- DynamicString.constant("1")
- )
- )
- .build()
- )
- },
- {
- setPlaceholder(
- ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
- .setShortText(
- ComplicationText(
- Random.nextInt().toString(), // Ignored when there's an expression.
- DynamicString.constant("2")
- )
- )
- .build()
- )
- },
+ { setPlaceholder(expressionData("1")) },
+ { setPlaceholder(expressionData("2")) },
),
-
- // Not ignored without an expression.
PLACEHOLDER_NO_EXPRESSION(
- {
- setPlaceholder(
- ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
- .setShortText(plainText("1"))
- .build()
- )
- },
- {
- setPlaceholder(
- ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
- .setShortText(plainText("2"))
- .build()
- )
- },
+ { setPlaceholder(staticData("1")) },
+ { setPlaceholder(staticData("2")) },
+ ),
+ LIST_ENTRY_COLLECTION_EXPRESSION(
+ { setListEntryCollection(listOf(expressionData("1"))) },
+ { setListEntryCollection(listOf(expressionData("2"))) },
+ ),
+ LIST_ENTRY_COLLECTION_EXPRESSION_DIFFERENT_SIZE(
+ { setListEntryCollection(listOf(expressionData("1"), expressionData("1"))) },
+ { setListEntryCollection(listOf(expressionData("1"))) },
+ ),
+ LIST_ENTRY_COLLECTION_NO_EXPRESSION(
+ { setListEntryCollection(listOf(staticData("1"))) },
+ { setListEntryCollection(listOf(staticData("2"))) },
+ ),
+ LIST_ENTRY_COLLECTION_NO_EXPRESSION_DIFFERENT_SIZE(
+ { setListEntryCollection(listOf(staticData("1"), staticData("1"))) },
+ { setListEntryCollection(listOf(staticData("1"))) },
),
;
- private val base = ComplicationData.TYPE_NO_DATA
+ val base = ComplicationData.Builder(TYPE_NO_DATA).build()
/** Builds a [ComplicationData] with the first variation. */
fun buildOne() = ComplicationData.Builder(base).apply { setterOne(this) }.build()
@@ -538,6 +406,24 @@
.withMessage("${scenario.name} does not unevaluated equal another")
.that(scenario.buildOne().equalsUnevaluated(scenario.buildTwo()))
.isFalse()
+ expect
+ .withMessage("${scenario.name} does not unevaluated equal unset")
+ .that(scenario.buildOne().equalsUnevaluated(scenario.base))
+ .isFalse()
}
}
+
+ private companion object {
+ fun staticData(value: String) =
+ ComplicationData.Builder(TYPE_SHORT_TEXT).setShortText(plainText(value)).build()
+
+ fun expressionData(value: String) =
+ ComplicationData.Builder(TYPE_SHORT_TEXT).setShortText(expressionText(value)).build()
+
+ fun expressionText(value: String) =
+ ComplicationText(
+ Random.nextInt().toString(), // Ignored when there's an expression.
+ DynamicString.constant(value)
+ )
+ }
}
diff --git a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluatorTest.kt b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluatorTest.kt
index c913c25..3a22917 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluatorTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluatorTest.kt
@@ -17,6 +17,8 @@
package androidx.wear.watchface.complications.data
import android.support.wearable.complications.ComplicationData as WireComplicationData
+import android.support.wearable.complications.ComplicationData.Companion.TYPE_NO_DATA
+import android.support.wearable.complications.ComplicationData.Companion.TYPE_SHORT_TEXT
import android.support.wearable.complications.ComplicationText as WireComplicationText
import android.util.Log
import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
@@ -58,10 +60,7 @@
@Test
fun evaluate_noExpression_returnsUnevaluated() = runBlocking {
- val data =
- WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
- .setRangedValue(10f)
- .build()
+ val data = WireComplicationData.Builder(TYPE_NO_DATA).setRangedValue(10f).build()
val evaluator = ComplicationDataExpressionEvaluator()
@@ -81,7 +80,7 @@
) {
SET_IMMEDIATELY_WHEN_ALL_DATA_AVAILABLE(
expressed =
- WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
+ WireComplicationData.Builder(TYPE_NO_DATA)
.setRangedValueExpression(DynamicFloat.constant(1f))
.setLongText(WireComplicationText(DynamicString.constant("Long Text")))
.setLongTitle(WireComplicationText(DynamicString.constant("Long Title")))
@@ -90,23 +89,29 @@
.setContentDescription(
WireComplicationText(DynamicString.constant("Description"))
)
- .build(),
+ .setPlaceholder(constantData("Placeholder"))
+ .setListEntryCollection(listOf(constantData("List")))
+ .build()
+ .also { it.setTimelineEntryCollection(listOf(constantData("Timeline"))) },
states = listOf(),
evaluated =
listOf(
- WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
+ WireComplicationData.Builder(TYPE_NO_DATA)
.setRangedValue(1f)
.setLongText(WireComplicationText("Long Text"))
.setLongTitle(WireComplicationText("Long Title"))
.setShortText(WireComplicationText("Short Text"))
.setShortTitle(WireComplicationText("Short Title"))
.setContentDescription(WireComplicationText("Description"))
+ .setPlaceholder(evaluatedData("Placeholder"))
+ .setListEntryCollection(listOf(evaluatedData("List")))
.build()
+ .also { it.setTimelineEntryCollection(listOf(evaluatedData("Timeline"))) },
),
),
SET_ONLY_AFTER_ALL_FIELDS_EVALUATED(
expressed =
- WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
+ WireComplicationData.Builder(TYPE_NO_DATA)
.setRangedValueExpression(DynamicFloat.fromState("ranged_value"))
.setLongText(WireComplicationText(DynamicString.fromState("long_text")))
.setLongTitle(WireComplicationText(DynamicString.fromState("long_title")))
@@ -115,7 +120,10 @@
.setContentDescription(
WireComplicationText(DynamicString.fromState("description"))
)
- .build(),
+ .setPlaceholder(stateData("placeholder"))
+ .setListEntryCollection(listOf(stateData("list")))
+ .build()
+ .also { it.setTimelineEntryCollection(listOf(stateData("timeline"))) },
states =
aggregate(
// Each map piles on top of the previous ones.
@@ -124,25 +132,38 @@
mapOf("long_title" to StateEntryValue.fromString("Long Title")),
mapOf("short_text" to StateEntryValue.fromString("Short Text")),
mapOf("short_title" to StateEntryValue.fromString("Short Title")),
- // Only the last one will trigger an evaluated data.
mapOf("description" to StateEntryValue.fromString("Description")),
+ mapOf("placeholder" to StateEntryValue.fromString("Placeholder")),
+ mapOf("list" to StateEntryValue.fromString("List")),
+ mapOf("timeline" to StateEntryValue.fromString("Timeline")),
+ // Only the last one will trigger an evaluated data.
),
evaluated =
listOf(
- INVALID_DATA, // Before state is available.
- WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
+ // Before any state is available.
+ INVALID_DATA,
+ // INVALID_DATA with placeholder, after it's available (and others aren't).
+ WireComplicationData.Builder(INVALID_DATA)
+ .setPlaceholder(evaluatedData("Placeholder"))
+ .build(),
+ // Evaluated data with after everything is available.
+ WireComplicationData.Builder(TYPE_NO_DATA)
.setRangedValue(1f)
.setLongText(WireComplicationText("Long Text"))
.setLongTitle(WireComplicationText("Long Title"))
.setShortText(WireComplicationText("Short Text"))
.setShortTitle(WireComplicationText("Short Title"))
.setContentDescription(WireComplicationText("Description"))
+ // Not trimmed for TYPE_NO_DATA.
+ .setPlaceholder(evaluatedData("Placeholder"))
+ .setListEntryCollection(listOf(evaluatedData("List")))
.build()
+ .also { it.setTimelineEntryCollection(listOf(evaluatedData("Timeline"))) },
),
),
SET_TO_EVALUATED_IF_ALL_FIELDS_VALID(
expressed =
- WireComplicationData.Builder(WireComplicationData.TYPE_SHORT_TEXT)
+ WireComplicationData.Builder(TYPE_SHORT_TEXT)
.setShortTitle(WireComplicationText(DynamicString.fromState("valid")))
.setShortText(WireComplicationText(DynamicString.fromState("valid")))
.build(),
@@ -153,7 +174,7 @@
evaluated =
listOf(
INVALID_DATA, // Before state is available.
- WireComplicationData.Builder(WireComplicationData.TYPE_SHORT_TEXT)
+ WireComplicationData.Builder(TYPE_SHORT_TEXT)
.setShortTitle(WireComplicationText("Valid"))
.setShortText(WireComplicationText("Valid"))
.build(),
@@ -161,7 +182,7 @@
),
SET_TO_NO_DATA_IF_FIRST_STATE_IS_INVALID(
expressed =
- WireComplicationData.Builder(WireComplicationData.TYPE_SHORT_TEXT)
+ WireComplicationData.Builder(TYPE_SHORT_TEXT)
.setShortTitle(WireComplicationText(DynamicString.fromState("valid")))
.setShortText(WireComplicationText(DynamicString.fromState("invalid")))
.build(),
@@ -177,7 +198,7 @@
),
SET_TO_NO_DATA_IF_LAST_STATE_IS_INVALID(
expressed =
- WireComplicationData.Builder(WireComplicationData.TYPE_SHORT_TEXT)
+ WireComplicationData.Builder(TYPE_SHORT_TEXT)
.setShortTitle(WireComplicationText(DynamicString.fromState("valid")))
.setShortText(WireComplicationText(DynamicString.fromState("invalid")))
.build(),
@@ -192,13 +213,43 @@
evaluated =
listOf(
INVALID_DATA, // Before state is available.
- WireComplicationData.Builder(WireComplicationData.TYPE_SHORT_TEXT)
+ WireComplicationData.Builder(TYPE_SHORT_TEXT)
.setShortTitle(WireComplicationText("Valid"))
.setShortText(WireComplicationText("Valid"))
.build(),
INVALID_DATA, // After it was invalidated.
),
),
+ SET_TO_EVALUATED_WITHOUT_PLACEHOLDER_IF_NOT_NO_DATA(
+ expressed =
+ WireComplicationData.Builder(TYPE_SHORT_TEXT)
+ .setShortText(WireComplicationText("Text"))
+ .setPlaceholder(evaluatedData("Placeholder"))
+ .build(),
+ states = listOf(),
+ evaluated =
+ listOf(
+ // No placeholder.
+ WireComplicationData.Builder(TYPE_SHORT_TEXT)
+ .setShortText(WireComplicationText("Text"))
+ .build(),
+ )
+ ),
+ SET_TO_EVALUATED_WITHOUT_PLACEHOLDER_EVEN_IF_PLACEHOLDER_INVALID_IF_NOT_NO_DATA(
+ expressed =
+ WireComplicationData.Builder(TYPE_SHORT_TEXT)
+ .setShortText(WireComplicationText("Text"))
+ .setPlaceholder(stateData("placeholder"))
+ .build(),
+ states = listOf(), // placeholder state not set.
+ evaluated =
+ listOf(
+ // No placeholder.
+ WireComplicationData.Builder(TYPE_SHORT_TEXT)
+ .setShortText(WireComplicationText("Text"))
+ .build(),
+ )
+ ),
}
@Test
@@ -231,7 +282,7 @@
@Test
fun evaluate_cancelled_cleansUp() = runBlocking {
val expressed =
- WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
+ WireComplicationData.Builder(TYPE_NO_DATA)
.setRangedValueExpression(
// Uses TimeGateway, which needs cleaning up.
DynamicInstant.withSecondsPrecision(Instant.EPOCH)
@@ -262,19 +313,22 @@
@Test
fun evaluate_keepExpression_doesNotTrimUnevaluatedExpression() = runBlocking {
val expressed =
- WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
+ WireComplicationData.Builder(TYPE_NO_DATA)
.setRangedValueExpression(DynamicFloat.constant(1f))
.setLongText(WireComplicationText(DynamicString.constant("Long Text")))
.setLongTitle(WireComplicationText(DynamicString.constant("Long Title")))
.setShortText(WireComplicationText(DynamicString.constant("Short Text")))
.setShortTitle(WireComplicationText(DynamicString.constant("Short Title")))
.setContentDescription(WireComplicationText(DynamicString.constant("Description")))
+ .setPlaceholder(constantData("Placeholder"))
+ .setListEntryCollection(listOf(constantData("List")))
.build()
+ .also { it.setTimelineEntryCollection(listOf(constantData("Timeline"))) }
val evaluator = ComplicationDataExpressionEvaluator(keepExpression = true)
assertThat(evaluator.evaluate(expressed).firstOrNull())
.isEqualTo(
- WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
+ WireComplicationData.Builder(TYPE_NO_DATA)
.setRangedValue(1f)
.setRangedValueExpression(DynamicFloat.constant(1f))
.setLongText(
@@ -292,6 +346,29 @@
.setContentDescription(
WireComplicationText("Description", DynamicString.constant("Description"))
)
+ .setPlaceholder(evaluatedWithConstantData("Placeholder"))
+ .setListEntryCollection(listOf(evaluatedWithConstantData("List")))
+ .build()
+ .also {
+ it.setTimelineEntryCollection(listOf(evaluatedWithConstantData("Timeline")))
+ },
+ )
+ }
+
+ @Test
+ fun evaluate_keepExpressionNotNoData_doesNotTrimPlaceholder() = runBlocking {
+ val expressed =
+ WireComplicationData.Builder(TYPE_SHORT_TEXT)
+ .setShortText(WireComplicationText("Text"))
+ .setPlaceholder(evaluatedData("Placeholder"))
+ .build()
+ val evaluator = ComplicationDataExpressionEvaluator(keepExpression = true)
+
+ assertThat(evaluator.evaluate(expressed).firstOrNull())
+ .isEqualTo(
+ WireComplicationData.Builder(TYPE_SHORT_TEXT)
+ .setShortText(WireComplicationText("Text"))
+ .setPlaceholder(evaluatedData("Placeholder"))
.build()
)
}
@@ -300,5 +377,25 @@
/** Converts `[{a: A}, {b: B}, {c: C}]` to `[{a: A}, {a: A, b: B}, {a: A, b: B, c: C}]`. */
fun <K, V> aggregate(vararg maps: Map<K, V>): List<Map<K, V>> =
maps.fold(listOf()) { acc, map -> acc + ((acc.lastOrNull() ?: mapOf()) + map) }
+
+ fun constantData(value: String) =
+ WireComplicationData.Builder(TYPE_NO_DATA)
+ .setLongText(WireComplicationText(DynamicString.constant(value)))
+ .build()
+
+ fun stateData(value: String) =
+ WireComplicationData.Builder(TYPE_NO_DATA)
+ .setLongText(WireComplicationText(DynamicString.fromState(value)))
+ .build()
+
+ fun evaluatedData(value: String) =
+ WireComplicationData.Builder(TYPE_NO_DATA)
+ .setLongText(WireComplicationText(value))
+ .build()
+
+ fun evaluatedWithConstantData(value: String) =
+ WireComplicationData.Builder(TYPE_NO_DATA)
+ .setLongText(WireComplicationText(value, DynamicString.constant(value)))
+ .build()
}
}
diff --git a/wear/watchface/watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/ComplicationStyle.kt b/wear/watchface/watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/ComplicationStyle.kt
index b771fdb..95542cf 100644
--- a/wear/watchface/watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/ComplicationStyle.kt
+++ b/wear/watchface/watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/ComplicationStyle.kt
@@ -27,10 +27,7 @@
/** Defines attributes to customize appearance of rendered [ ]. */
public class ComplicationStyle {
- /**
- * Constants used to define border styles for complicationSlots.
- *
- */
+ /** Constants used to define border styles for complicationSlots. */
@Retention(AnnotationRetention.SOURCE)
@IntDef(BORDER_STYLE_NONE, BORDER_STYLE_SOLID, BORDER_STYLE_DASHED)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -194,6 +191,7 @@
}
isDirty = true
}
+
/** The dash width to be used when drawing borders of type [.BORDER_STYLE_DASHED]. */
public var borderDashWidth: Int
@Px get() = mBorderDashWidth
@@ -209,6 +207,7 @@
mBorderDashGap = borderDashGap
isDirty = true
}
+
/**
* The border radius to be applied to the corners of the bounds of the complication in active
* mode. Border radius will be limited to the half of width or height, depending on which one is
@@ -282,10 +281,7 @@
isDirty = true
}
- /**
- * Returns a copy of the ComplicationStyle [tint]ed by [tintColor].
- *
- */
+ /** Returns a copy of the ComplicationStyle [tint]ed by [tintColor]. */
@RestrictTo(RestrictTo.Scope.LIBRARY)
fun asTinted(tintColor: Int): ComplicationStyle =
ComplicationStyle(this).apply {
diff --git a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetriever.kt b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetriever.kt
index 873ef14..af4e203 100644
--- a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetriever.kt
+++ b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetriever.kt
@@ -397,10 +397,7 @@
return result
}
- /**
- * Converts this value to [WireComplicationProviderInfo] object used for serialization.
- *
- */
+ /** Converts this value to [WireComplicationProviderInfo] object used for serialization. */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public fun toWireComplicationProviderInfo(): WireComplicationProviderInfo =
WireComplicationProviderInfo(
diff --git a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationSlotBounds.kt b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationSlotBounds.kt
index ec9b539..d3770b2 100644
--- a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationSlotBounds.kt
+++ b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/ComplicationSlotBounds.kt
@@ -129,7 +129,6 @@
* RectF>, backfilling with empty [RectF]s. This method is necessary because there can be a
* skew between the version of the library between the watch face and the system which would
* otherwise be problematic if new complication types have been introduced.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun createFromPartialMap(
diff --git a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/SystemDataSources.kt b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/SystemDataSources.kt
index a50594f..b3d620f 100644
--- a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/SystemDataSources.kt
+++ b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/SystemDataSources.kt
@@ -179,10 +179,7 @@
public const val DATA_SOURCE_DAY_AND_DATE: Int = 16
}
- /**
- * System complication data source id as defined in [SystemDataSources].
- *
- */
+ /** System complication data source id as defined in [SystemDataSources]. */
@IntDef(
NO_DATA_SOURCE,
DATA_SOURCE_WATCH_BATTERY,
diff --git a/wear/watchface/watchface-complications/src/test/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetrieverTest.kt b/wear/watchface/watchface-complications/src/test/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetrieverTest.kt
index 4108ebf..f40e087 100644
--- a/wear/watchface/watchface-complications/src/test/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetrieverTest.kt
+++ b/wear/watchface/watchface-complications/src/test/java/androidx/wear/watchface/complications/ComplicationDataSourceInfoRetrieverTest.kt
@@ -33,11 +33,11 @@
import androidx.wear.watchface.complications.data.SmallImageComplicationData
import com.google.common.truth.Truth.assertThat
import kotlin.jvm.java
-import org.mockito.Mockito
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito
@RunWith(SharedRobolectricTestRunner::class)
public class ComplicationDataSourceInfoRetrieverTest {
@@ -58,16 +58,16 @@
val testData: ComplicationData =
LongTextComplicationData.Builder(
- PlainComplicationText.Builder("Test Text").build(),
- ComplicationText.Companion.EMPTY
- )
+ PlainComplicationText.Builder("Test Text").build(),
+ ComplicationText.Companion.EMPTY
+ )
.build()
Mockito.doAnswer {
- val callback = it.arguments[2] as IPreviewComplicationDataCallback
- callback.updateComplicationData(testData.asWireComplicationData())
- true
- }
+ val callback = it.arguments[2] as IPreviewComplicationDataCallback
+ callback.updateComplicationData(testData.asWireComplicationData())
+ true
+ }
.`when`(mockService)
.requestPreviewComplicationData(
eq(component),
@@ -82,13 +82,13 @@
)!!
assertThat(previewData.type).isEqualTo(type)
assertThat(
- (previewData as LongTextComplicationData)
- .text
- .getTextAt(
- ApplicationProvider.getApplicationContext<Context>().resources,
- java.time.Instant.EPOCH
- )
- )
+ (previewData as LongTextComplicationData)
+ .text
+ .getTextAt(
+ ApplicationProvider.getApplicationContext<Context>().resources,
+ java.time.Instant.EPOCH
+ )
+ )
.isEqualTo("Test Text")
}
}
@@ -103,10 +103,10 @@
Mockito.`when`(mockService.asBinder()).thenReturn(mockBinder)
Mockito.doAnswer {
- val callback = it.arguments[2] as IPreviewComplicationDataCallback
- callback.updateComplicationData(null)
- true
- }
+ val callback = it.arguments[2] as IPreviewComplicationDataCallback
+ callback.updateComplicationData(null)
+ true
+ }
.`when`(mockService)
.requestPreviewComplicationData(
eq(component),
@@ -115,11 +115,11 @@
)
assertThat(
- complicationDataSourceInfoRetriever.retrievePreviewComplicationData(
- component,
- type
+ complicationDataSourceInfoRetriever.retrievePreviewComplicationData(
+ component,
+ type
+ )
)
- )
.isNull()
}
}
@@ -134,11 +134,11 @@
Mockito.`when`(mockService.asBinder()).thenReturn(mockBinder)
assertThat(
- complicationDataSourceInfoRetriever.retrievePreviewComplicationData(
- component,
- type
+ complicationDataSourceInfoRetriever.retrievePreviewComplicationData(
+ component,
+ type
+ )
)
- )
.isNull()
}
}
@@ -160,11 +160,11 @@
)
assertThat(
- complicationDataSourceInfoRetriever.retrievePreviewComplicationData(
- component,
- type
+ complicationDataSourceInfoRetriever.retrievePreviewComplicationData(
+ component,
+ type
+ )
)
- )
.isNull()
}
}
@@ -192,21 +192,21 @@
val icon = android.graphics.drawable.Icon.createWithContentUri("icon")
val shortTextPreviewData =
ComplicationDataSourceInfo(
- "applicationName",
- "complicationName",
- icon,
- ComplicationType.SHORT_TEXT,
- componentName = null
- )
+ "applicationName",
+ "complicationName",
+ icon,
+ ComplicationType.SHORT_TEXT,
+ componentName = null
+ )
.fallbackPreviewData as ShortTextComplicationData
assertThat(shortTextPreviewData.text.getTextAt(resources, java.time.Instant.EPOCH))
.isEqualTo("complic")
assertThat(
- shortTextPreviewData.contentDescription!!.getTextAt(
- resources,
- java.time.Instant.EPOCH
+ shortTextPreviewData.contentDescription!!.getTextAt(
+ resources,
+ java.time.Instant.EPOCH
+ )
)
- )
.isEqualTo("complicationName")
assertThat(shortTextPreviewData.monochromaticImage!!.image).isEqualTo(icon)
}
@@ -216,21 +216,21 @@
val icon = android.graphics.drawable.Icon.createWithContentUri("icon")
val longTextPreviewData =
ComplicationDataSourceInfo(
- "applicationName",
- "complicationName",
- icon,
- ComplicationType.LONG_TEXT,
- componentName = null
- )
+ "applicationName",
+ "complicationName",
+ icon,
+ ComplicationType.LONG_TEXT,
+ componentName = null
+ )
.fallbackPreviewData as LongTextComplicationData
assertThat(longTextPreviewData.text.getTextAt(resources, java.time.Instant.EPOCH))
.isEqualTo("complicationName")
assertThat(
- longTextPreviewData.contentDescription!!.getTextAt(
- resources,
- java.time.Instant.EPOCH
+ longTextPreviewData.contentDescription!!.getTextAt(
+ resources,
+ java.time.Instant.EPOCH
+ )
)
- )
.isEqualTo("complicationName")
assertThat(longTextPreviewData.monochromaticImage!!.image).isEqualTo(icon)
}
@@ -240,20 +240,20 @@
val icon = android.graphics.drawable.Icon.createWithContentUri("icon")
val smallImagePreviewData =
ComplicationDataSourceInfo(
- "applicationName",
- "complicationName",
- icon,
- ComplicationType.SMALL_IMAGE,
- componentName = null
- )
+ "applicationName",
+ "complicationName",
+ icon,
+ ComplicationType.SMALL_IMAGE,
+ componentName = null
+ )
.fallbackPreviewData as SmallImageComplicationData
assertThat(smallImagePreviewData.smallImage.image).isEqualTo(icon)
assertThat(
- smallImagePreviewData.contentDescription!!.getTextAt(
- resources,
- java.time.Instant.EPOCH
+ smallImagePreviewData.contentDescription!!.getTextAt(
+ resources,
+ java.time.Instant.EPOCH
+ )
)
- )
.isEqualTo("complicationName")
}
@@ -262,20 +262,20 @@
val icon = android.graphics.drawable.Icon.createWithContentUri("icon")
val photoImagePreviewData =
ComplicationDataSourceInfo(
- "applicationName",
- "complicationName",
- icon,
- ComplicationType.PHOTO_IMAGE,
- componentName = null
- )
+ "applicationName",
+ "complicationName",
+ icon,
+ ComplicationType.PHOTO_IMAGE,
+ componentName = null
+ )
.fallbackPreviewData as PhotoImageComplicationData
assertThat(photoImagePreviewData.photoImage).isEqualTo(icon)
assertThat(
- photoImagePreviewData.contentDescription!!.getTextAt(
- resources,
- java.time.Instant.EPOCH
+ photoImagePreviewData.contentDescription!!.getTextAt(
+ resources,
+ java.time.Instant.EPOCH
+ )
)
- )
.isEqualTo("complicationName")
}
@@ -284,20 +284,20 @@
val icon = android.graphics.drawable.Icon.createWithContentUri("icon")
val monochromaticImagePreviewData =
ComplicationDataSourceInfo(
- "applicationName",
- "complicationName",
- icon,
- ComplicationType.MONOCHROMATIC_IMAGE,
- componentName = null
- )
+ "applicationName",
+ "complicationName",
+ icon,
+ ComplicationType.MONOCHROMATIC_IMAGE,
+ componentName = null
+ )
.fallbackPreviewData as MonochromaticImageComplicationData
assertThat(monochromaticImagePreviewData.monochromaticImage.image).isEqualTo(icon)
assertThat(
- monochromaticImagePreviewData.contentDescription!!.getTextAt(
- resources,
- java.time.Instant.EPOCH
+ monochromaticImagePreviewData.contentDescription!!.getTextAt(
+ resources,
+ java.time.Instant.EPOCH
+ )
)
- )
.isEqualTo("complicationName")
}
@@ -306,12 +306,12 @@
val icon = android.graphics.drawable.Icon.createWithContentUri("icon")
val rangedValuePreviewData =
ComplicationDataSourceInfo(
- "applicationName",
- "complicationName",
- icon,
- ComplicationType.RANGED_VALUE,
- componentName = null
- )
+ "applicationName",
+ "complicationName",
+ icon,
+ ComplicationType.RANGED_VALUE,
+ componentName = null
+ )
.fallbackPreviewData as RangedValueComplicationData
assertThat(rangedValuePreviewData.min).isEqualTo(0.0f)
assertThat(rangedValuePreviewData.max).isEqualTo(100.0f)
@@ -320,11 +320,11 @@
.isEqualTo("complicationName")
assertThat(rangedValuePreviewData.monochromaticImage!!.image).isEqualTo(icon)
assertThat(
- rangedValuePreviewData.contentDescription!!.getTextAt(
- resources,
- java.time.Instant.EPOCH
+ rangedValuePreviewData.contentDescription!!.getTextAt(
+ resources,
+ java.time.Instant.EPOCH
+ )
)
- )
.isEqualTo("complicationName")
}
@@ -332,27 +332,30 @@
public fun complicationDataSourceInfo_equals() {
val icon = android.graphics.drawable.Icon.createWithContentUri("icon")
val icon2 = android.graphics.drawable.Icon.createWithContentUri("icon")
- val a = ComplicationDataSourceInfo(
+ val a =
+ ComplicationDataSourceInfo(
"applicationName",
"complicationName",
icon,
ComplicationType.RANGED_VALUE,
componentName = null
)
- val b = ComplicationDataSourceInfo(
+ val b =
+ ComplicationDataSourceInfo(
"applicationName",
"complicationName",
icon2,
ComplicationType.RANGED_VALUE,
componentName = null
)
- val c = ComplicationDataSourceInfo(
- "applicationName2",
- "complicationName2",
- icon,
- ComplicationType.RANGED_VALUE,
- componentName = null
- )
+ val c =
+ ComplicationDataSourceInfo(
+ "applicationName2",
+ "complicationName2",
+ icon,
+ ComplicationType.RANGED_VALUE,
+ componentName = null
+ )
// Test two identical ComplicationDataSourceInfo with different references.
assertThat(a).isEqualTo(b)
diff --git a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/Constants.kt b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/Constants.kt
index d4422b0..217353a 100644
--- a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/Constants.kt
+++ b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/Constants.kt
@@ -19,10 +19,7 @@
import android.app.WallpaperManager
import androidx.annotation.RestrictTo
-/**
- * Shared constants between client and implementation.
- *
- */
+/** Shared constants between client and implementation. */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class Constants {
// Not instantiable.
diff --git a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/ParcelableWrapper.kt b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/ParcelableWrapper.kt
index db2f239..16c2957 100644
--- a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/ParcelableWrapper.kt
+++ b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/ParcelableWrapper.kt
@@ -21,10 +21,7 @@
import android.os.Parcelable
import androidx.annotation.RestrictTo
-/**
- * Wraps a Parcelable.
- *
- */
+/** Wraps a Parcelable. */
@SuppressLint("BanParcelableUsage")
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
class ParcelableWrapper(val parcelable: Parcelable) : Parcelable {
diff --git a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/SharedMemoryImage.kt b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/SharedMemoryImage.kt
index ad83ad4..4252e0b 100644
--- a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/SharedMemoryImage.kt
+++ b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/SharedMemoryImage.kt
@@ -27,7 +27,6 @@
/**
* This class requires API level 27 and is only intended for use in conjunction with
* wear-watchface-client which also requires API level 27.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class SharedMemoryImage {
diff --git a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/WatchFaceStyle.kt b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/WatchFaceStyle.kt
index ce40b16..a29c0ce 100644
--- a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/WatchFaceStyle.kt
+++ b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/WatchFaceStyle.kt
@@ -32,7 +32,6 @@
* of your [WatchFaceService.Engine.onCreate] override.
*
* <p>To construct a WatchFaceStyle use [WatchFaceStyle.Builder].
- *
*/
@SuppressWarnings("BanParcelableUsage")
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
diff --git a/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/ComplicationConfigFragment.kt b/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/ComplicationConfigFragment.kt
index 8f354ad..cebcf7b 100644
--- a/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/ComplicationConfigFragment.kt
+++ b/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/ComplicationConfigFragment.kt
@@ -59,10 +59,7 @@
}
}
-/**
- * Configuration view for watch faces with multiple complicationSlots.
- *
- */
+/** Configuration view for watch faces with multiple complicationSlots. */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@SuppressWarnings(
"ViewConstructor", // Internal view, not intended for use by tools.
@@ -82,8 +79,8 @@
// TODO(alexclarke): This button is a Rect which makes the tap animation look bad.
if (
entry.value.fixedComplicationDataSource ||
- !entry.value.isEnabled ||
- entry.key == watchFaceConfigActivity.editorSession.backgroundComplicationSlotId
+ !entry.value.isEnabled ||
+ entry.key == watchFaceConfigActivity.editorSession.backgroundComplicationSlotId
) {
// Do not create a button for fixed complicationSlots, disabled complicationSlots,
// or background complicationSlots.
diff --git a/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/StyleConfigFragment.kt b/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/StyleConfigFragment.kt
index 9d0825c..e66ab3b 100644
--- a/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/StyleConfigFragment.kt
+++ b/wear/watchface/watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/StyleConfigFragment.kt
@@ -40,9 +40,9 @@
import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting
import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption
import androidx.wear.watchface.style.UserStyleSetting.CustomValueUserStyleSetting
-import androidx.wear.watchface.style.UserStyleSetting.LargeCustomValueUserStyleSetting
import androidx.wear.watchface.style.UserStyleSetting.DoubleRangeUserStyleSetting
import androidx.wear.watchface.style.UserStyleSetting.DoubleRangeUserStyleSetting.DoubleRangeOption
+import androidx.wear.watchface.style.UserStyleSetting.LargeCustomValueUserStyleSetting
import androidx.wear.watchface.style.UserStyleSetting.ListUserStyleSetting
import androidx.wear.watchface.style.UserStyleSetting.LongRangeUserStyleSetting
import androidx.wear.watchface.style.UserStyleSetting.LongRangeUserStyleSetting.LongRangeOption
diff --git a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
index 32cdbdc..f99c2d7 100644
--- a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
+++ b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
@@ -600,24 +600,24 @@
val mockSurfaceHolder = `mock`(SurfaceHolder::class.java)
`when`(mockSurfaceHolder.surfaceFrame).thenReturn(screenBounds)
@Suppress("Deprecation")
- val fakeRenderer = object : Renderer.CanvasRenderer(
- mockSurfaceHolder,
- userStyleRepository,
- MutableWatchState().asWatchState(),
- CanvasType.HARDWARE,
- interactiveDrawModeUpdateDelayMillis = 16,
- clearWithBackgroundTintBeforeRenderingHighlightLayer = false
- ) {
- override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {
- }
+ val fakeRenderer =
+ object :
+ Renderer.CanvasRenderer(
+ mockSurfaceHolder,
+ userStyleRepository,
+ MutableWatchState().asWatchState(),
+ CanvasType.HARDWARE,
+ interactiveDrawModeUpdateDelayMillis = 16,
+ clearWithBackgroundTintBeforeRenderingHighlightLayer = false
+ ) {
+ override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {}
- override fun renderHighlightLayer(
- canvas: Canvas,
- bounds: Rect,
- zonedDateTime: ZonedDateTime
- ) {
+ override fun renderHighlightLayer(
+ canvas: Canvas,
+ bounds: Rect,
+ zonedDateTime: ZonedDateTime
+ ) {}
}
- }
val complicationSlotsManager =
ComplicationSlotsManager(complicationSlots, userStyleRepository, fakeRenderer)
@@ -1835,7 +1835,6 @@
EditorService.globalEditorService.unregisterObserver(observerId)
}
- @SdkSuppress(maxSdkVersion = 32) // b/275361339
@Test
@Suppress("Deprecation") // userStyleSettings
public fun commit_headless() {
@@ -1887,7 +1886,6 @@
EditorService.globalEditorService.unregisterObserver(observerId)
}
- @SdkSuppress(maxSdkVersion = 32) // b/275361339
@SuppressLint("NewApi")
@Suppress("Deprecation") // userStyleSettings
@Test
diff --git a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt
index 6fd3d02..1eacf82 100644
--- a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt
+++ b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt
@@ -716,10 +716,7 @@
*/
public val userStyle: StateFlow<UserStyle> by CurrentUserStyleRepository::mutableUserStyle
- /**
- * The UserStyle options must be from the supplied [UserStyleSchema].
- *
- */
+ /** The UserStyle options must be from the supplied [UserStyleSchema]. */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public fun updateUserStyle(newUserStyle: UserStyle) {
validateUserStyle(newUserStyle)
diff --git a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
index 8ce3c6c..a79956a 100644
--- a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
+++ b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
@@ -242,7 +242,6 @@
* used.
*
* Note this method can be slow.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public fun estimateWireSizeInBytesAndValidateIconDimensions(
@@ -1028,6 +1027,7 @@
nameResourceId?.let { dos.writeInt(it) }
screenReaderNameResourceId?.let { dos.writeInt(it) }
}
+
/**
* Constructs a [ComplicationSlotOverlay].Builder.
*
diff --git a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt
index 1a449b9..c98331c 100644
--- a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt
+++ b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt
@@ -26,8 +26,8 @@
import androidx.wear.watchface.style.UserStyleSetting.BooleanUserStyleSetting
import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting
import androidx.wear.watchface.style.UserStyleSetting.CustomValueUserStyleSetting
-import androidx.wear.watchface.style.UserStyleSetting.LargeCustomValueUserStyleSetting
import androidx.wear.watchface.style.UserStyleSetting.DoubleRangeUserStyleSetting
+import androidx.wear.watchface.style.UserStyleSetting.LargeCustomValueUserStyleSetting
import androidx.wear.watchface.style.UserStyleSetting.ListUserStyleSetting
import androidx.wear.watchface.style.UserStyleSetting.ListUserStyleSetting.ListOption
import androidx.wear.watchface.style.UserStyleSetting.LongRangeUserStyleSetting
@@ -194,10 +194,11 @@
listOf(WatchFaceLayer.BASE),
true
)
- val styleSetting4 = LargeCustomValueUserStyleSetting(
- listOf(WatchFaceLayer.BASE),
- "default".encodeToByteArray()
- )
+ val styleSetting4 =
+ LargeCustomValueUserStyleSetting(
+ listOf(WatchFaceLayer.BASE),
+ "default".encodeToByteArray()
+ )
val srcSchema =
UserStyleSchema(listOf(styleSetting1, styleSetting2, styleSetting3, styleSetting4))
diff --git a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/ComplicationHelperActivityTest.kt b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/ComplicationHelperActivityTest.kt
index 6550522..dcc2146 100644
--- a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/ComplicationHelperActivityTest.kt
+++ b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/ComplicationHelperActivityTest.kt
@@ -29,14 +29,14 @@
import androidx.wear.watchface.complications.data.ComplicationType.MONOCHROMATIC_IMAGE
import androidx.wear.watchface.complications.data.ComplicationType.SHORT_TEXT
import com.google.common.truth.Truth.assertThat
-import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.never
-import org.mockito.kotlin.verify
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
const val TIME_OUT_MILLIS = 500L
diff --git a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/WatchFaceServiceAndroidTest.kt b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/WatchFaceServiceAndroidTest.kt
index eafefe5..8c7e17e 100644
--- a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/WatchFaceServiceAndroidTest.kt
+++ b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/WatchFaceServiceAndroidTest.kt
@@ -21,7 +21,6 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
-import androidx.test.filters.SdkSuppress
import androidx.wear.watchface.control.InteractiveInstanceManager
import androidx.wear.watchface.style.UserStyleSchema
import androidx.wear.watchface.style.UserStyleSetting
@@ -39,7 +38,6 @@
InteractiveInstanceManager.setParameterlessEngine(null)
}
- @SdkSuppress(maxSdkVersion = 32) // b/275361339
@Test
fun measuresWatchFaceIconsFromCustomContext() {
val context: Context = ApplicationProvider.getApplicationContext()
diff --git a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt
index 53f13686..7bb5522 100644
--- a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt
+++ b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt
@@ -543,12 +543,15 @@
@Test
public fun createWatchFaceService_throwsOnInvalidClass() {
- assertThat(WatchFaceControlService()
- .createWatchFaceService(
- ComponentName(
- ApplicationProvider.getApplicationContext(),
- WatchFaceControlServiceTest::class.java
- )
- )).isNull()
+ assertThat(
+ WatchFaceControlService()
+ .createWatchFaceService(
+ ComponentName(
+ ApplicationProvider.getApplicationContext(),
+ WatchFaceControlServiceTest::class.java
+ )
+ )
+ )
+ .isNull()
}
}
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsReceiver.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsReceiver.kt
index 644a68a..a7130b91 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsReceiver.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsReceiver.kt
@@ -28,7 +28,6 @@
/**
* This class decouples [BroadcastEventObserver]s from the actual broadcast event receivers to make
* testing easier.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class BroadcastsReceiver
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
index 0def254..41de246 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
@@ -633,8 +633,7 @@
supportedTypes: List<ComplicationType>,
defaultDataSourcePolicy: DefaultComplicationDataSourcePolicy,
bounds: ComplicationSlotBounds,
- @Suppress("HiddenTypeParameter")
- boundingArc: BoundingArc,
+ @Suppress("HiddenTypeParameter") boundingArc: BoundingArc,
complicationTapFilter: ComplicationTapFilter =
object : ComplicationTapFilter {
override fun hitTest(
@@ -808,25 +807,28 @@
/** Constructs the [ComplicationSlot]. */
public fun build(): ComplicationSlot {
- require(defaultDataSourcePolicy.primaryDataSourceDefaultType == null ||
- defaultDataSourcePolicy.primaryDataSourceDefaultType in supportedTypes
+ require(
+ defaultDataSourcePolicy.primaryDataSourceDefaultType == null ||
+ defaultDataSourcePolicy.primaryDataSourceDefaultType in supportedTypes
) {
"defaultDataSourcePolicy.primaryDataSourceDefaultType " +
"${defaultDataSourcePolicy.primaryDataSourceDefaultType} must be in the" +
" supportedTypes list: $supportedTypes"
}
- require(defaultDataSourcePolicy.secondaryDataSourceDefaultType == null ||
- defaultDataSourcePolicy.secondaryDataSourceDefaultType in supportedTypes
+ require(
+ defaultDataSourcePolicy.secondaryDataSourceDefaultType == null ||
+ defaultDataSourcePolicy.secondaryDataSourceDefaultType in supportedTypes
) {
"defaultDataSourcePolicy.secondaryDataSourceDefaultType " +
"${defaultDataSourcePolicy.secondaryDataSourceDefaultType} must be in the" +
" supportedTypes list: $supportedTypes"
}
- require(defaultDataSourcePolicy.systemDataSourceFallbackDefaultType ==
- ComplicationType.NOT_CONFIGURED ||
- defaultDataSourcePolicy.systemDataSourceFallbackDefaultType in supportedTypes
+ require(
+ defaultDataSourcePolicy.systemDataSourceFallbackDefaultType ==
+ ComplicationType.NOT_CONFIGURED ||
+ defaultDataSourcePolicy.systemDataSourceFallbackDefaultType in supportedTypes
) {
"defaultDataSourcePolicy.systemDataSourceFallbackDefaultType " +
"${defaultDataSourcePolicy.systemDataSourceFallbackDefaultType} must be in " +
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt
index f996759..28a76f1 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt
@@ -175,7 +175,7 @@
) {
/** The [SurfaceHolder] that [renderInternal] will draw into. */
public var surfaceHolder: SurfaceHolder = surfaceHolder
- protected set
+ protected set
@OptIn(WatchFaceExperimental::class) private var pendingWatchFaceColors: WatchFaceColors? = null
private var pendingWatchFaceColorsSet = false
@@ -379,7 +379,7 @@
* @param zonedDateTime The [ZonedDateTime] to use when rendering the watch face
* @param renderParameters The [RenderParameters] to use when rendering the watch face
* @param screenShotSurfaceHolder The [SurfaceHolder] containing the [Surface] to render into.
- * This is assumed to have the same dimensions as the screen.
+ * This is assumed to have the same dimensions as the screen.
*/
@Suppress("HiddenAbstractMethod")
@UiThread
@@ -1448,13 +1448,14 @@
runBlocking {
glContextLock.withLock {
- val tempEglSurface = EGL14.eglCreateWindowSurface(
- eglDisplay,
- eglConfig,
- surfaceHolder.surface,
- eglSurfaceAttribList,
- 0
- )
+ val tempEglSurface =
+ EGL14.eglCreateWindowSurface(
+ eglDisplay,
+ eglConfig,
+ surfaceHolder.surface,
+ eglSurfaceAttribList,
+ 0
+ )
if (
!EGL14.eglMakeCurrent(
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
index c0cb13a..edb9c49 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
@@ -84,7 +84,6 @@
/**
* The type of watch face, whether it's digital or analog. This influences the time displayed for
* remote previews.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
@IntDef(value = [WatchFaceType.DIGITAL, WatchFaceType.ANALOG])
@@ -158,10 +157,7 @@
componentNameToEditorDelegate.clear()
}
- /**
- * For use by on watch face editors.
- *
- */
+ /** For use by on watch face editors. */
@JvmStatic
@UiThread
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -180,9 +176,7 @@
}
@UiThread
- internal fun createWatchFaceServiceOld(
- componentName: ComponentName
- ): WatchFaceService {
+ internal fun createWatchFaceServiceOld(componentName: ComponentName): WatchFaceService {
// Attempt to construct the class for the specified watchFaceName, failing if it either
// doesn't exist or isn't a [WatchFaceService].
val watchFaceServiceClass =
@@ -205,10 +199,13 @@
context: Context
): WatchFaceService {
// Resolve the WatchFaceControlService and construct WatchFaceService using its API
- val services = context.packageManager.queryIntentServices(Intent(
- WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
- setPackage(context.packageName)
- }, 0)
+ val services =
+ context.packageManager.queryIntentServices(
+ Intent(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
+ setPackage(context.packageName)
+ },
+ 0
+ )
if (services.size != 1)
throw IllegalArgumentException(
@@ -218,9 +215,7 @@
val watchFaceControlServiceClass =
Class.forName(services[0].serviceInfo.name)
- ?: throw IllegalArgumentException(
- "Can't find ${services[0].serviceInfo.name}"
- )
+ ?: throw IllegalArgumentException("Can't find ${services[0].serviceInfo.name}")
val watchFaceControlService =
watchFaceControlServiceClass.getConstructor().newInstance()
@@ -230,10 +225,7 @@
?: throw IllegalArgumentException("Can't create ${componentName.className}")
}
- /**
- * For use by on watch face editors.
- *
- */
+ /** For use by on watch face editors. */
@SuppressLint("NewApi")
@JvmStatic
@UiThread
@@ -243,25 +235,21 @@
params: HeadlessWatchFaceInstanceParams,
context: Context
): EditorDelegate {
- val watchFaceService = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- createWatchFaceService(componentName, context)
- } else {
- createWatchFaceServiceOld(componentName)
- }.apply {
- setContext(context)
- }
+ val watchFaceService =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ createWatchFaceService(componentName, context)
+ } else {
+ createWatchFaceServiceOld(componentName)
+ }
+ .apply { setContext(context) }
- val engine =
- watchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+ val engine = watchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
val headlessWatchFaceImpl = engine.createHeadlessInstance(params)
return engine.deferredWatchFaceImpl.await().WFEditorDelegate(headlessWatchFaceImpl)
}
}
- /**
- * Delegate used by on watch face editors.
- *
- */
+ /** Delegate used by on watch face editors. */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public interface EditorDelegate {
/** The [WatchFace]'s [UserStyleSchema]. */
@@ -306,10 +294,7 @@
)
}
- /**
- * Used to inform EditorSession about changes to [ComplicationSlot.configExtras].
- *
- */
+ /** Used to inform EditorSession about changes to [ComplicationSlot.configExtras]. */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public interface ComplicationSlotConfigExtrasChangeCallback {
public fun onComplicationSlotConfigExtrasChanged()
@@ -557,8 +542,7 @@
private val watchFaceHostApi: WatchFaceHostApi,
private val watchState: WatchState,
internal val currentUserStyleRepository: CurrentUserStyleRepository,
- @get:VisibleForTesting
- public var complicationSlotsManager: ComplicationSlotsManager,
+ @get:VisibleForTesting public var complicationSlotsManager: ComplicationSlotsManager,
internal val broadcastsObserver: BroadcastsObserver,
internal var broadcastsReceiver: BroadcastsReceiver?
) {
@@ -1176,19 +1160,20 @@
hostToken: IBinder,
width: Int,
height: Int
- ): RemoteWatchFaceView? = TraceEvent("WatchFaceImpl.createRemoteWatchFaceView").use {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- return CreateRemoteWatchFaceViewHelper.createRemoteWatchFaceView(
- watchFaceHostApi,
- this,
- hostToken,
- width,
- height
- )
- } else {
- return null
+ ): RemoteWatchFaceView? =
+ TraceEvent("WatchFaceImpl.createRemoteWatchFaceView").use {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ return CreateRemoteWatchFaceViewHelper.createRemoteWatchFaceView(
+ watchFaceHostApi,
+ this,
+ hostToken,
+ width,
+ height
+ )
+ } else {
+ return null
+ }
}
- }
@UiThread
@RequiresApi(27)
@@ -1286,27 +1271,26 @@
height: Int
): RemoteWatchFaceView {
val context = watchFaceHostApi.getContext()
- val host = SurfaceControlViewHost(
- context,
- context.getSystemService(WindowManager::class.java).defaultDisplay,
- hostToken
- )
+ val host =
+ SurfaceControlViewHost(
+ context,
+ context.getSystemService(WindowManager::class.java).defaultDisplay,
+ hostToken
+ )
val view = SurfaceView(context)
- view.layoutParams = WindowManager.LayoutParams(
- WindowManager.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
- WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
- PixelFormat.TRANSLUCENT
- ).apply {
- title = "RemoteWatchFaceView"
- }
+ view.layoutParams =
+ WindowManager.LayoutParams(
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+ PixelFormat.TRANSLUCENT
+ )
+ .apply { title = "RemoteWatchFaceView" }
host.setView(view, width, height)
- return RemoteWatchFaceView(
- view,
- host,
- watchFaceHostApi.getUiThreadCoroutineScope()
- ) { surfaceHolder, params ->
+ return RemoteWatchFaceView(view, host, watchFaceHostApi.getUiThreadCoroutineScope()) {
+ surfaceHolder,
+ params ->
val oldStyle = watchFaceImpl.currentUserStyleRepository.userStyle.value
val instant = Instant.ofEpochMilli(params.calendarTimeMillis)
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
index fce93a2..3f160d0 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
@@ -28,10 +28,7 @@
import java.time.Duration
import kotlinx.coroutines.CoroutineScope
-/**
- * The API [WatchFaceImpl] uses to communicate with the system.
- *
- */
+/** The API [WatchFaceImpl] uses to communicate with the system. */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public interface WatchFaceHostApi {
/** The [WatchFaceService.SystemTimeProvider]. */
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index 4cf18b94..0f9a4ba 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -429,17 +429,13 @@
}
}
- /**
- * The context used to resolve resources. Unlocks future work.
- *
- */
+ /** The context used to resolve resources. Unlocks future work. */
protected open val resourcesContext: Context
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) get() = this
/**
* Returns the id of the XmlSchemaAndComplicationSlotsDefinition XML resource or 0 if it can't
* be found.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@Suppress("DEPRECATION")
@@ -618,7 +614,6 @@
/**
* Override to force the watchface to be regarded as being visible. This must not be used in
* production code or significant battery life regressions may occur.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) open fun forceIsVisibleForTesting() = false
@@ -653,10 +648,7 @@
internal var backgroundThread: HandlerThread? = null
- /**
- * Interface for getting the current system time.
- *
- */
+ /** Interface for getting the current system time. */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public interface SystemTimeProvider {
/** Returns the current system time in milliseconds. */
@@ -719,6 +711,7 @@
/** [Choreographer] isn't supposed to be mocked, so we use a thin wrapper. */
internal interface ChoreographerWrapper {
fun postFrameCallback(callback: Choreographer.FrameCallback)
+
fun removeFrameCallback(callback: Choreographer.FrameCallback)
}
@@ -744,10 +737,7 @@
internal open fun cancelCoroutineScopesInOnDestroy() = true
- /**
- * This is open for use by tests, it allows them to inject a custom [SurfaceHolder].
- *
- */
+ /** This is open for use by tests, it allows them to inject a custom [SurfaceHolder]. */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public open fun getWallpaperSurfaceHolderOverride(): SurfaceHolder? = null
@@ -1402,10 +1392,11 @@
// probably will connect at a later time. In the latter case we should
// register a parameterless engine to allow the subsequent connection to
// succeed.
- pendingWallpaperInstance = InteractiveInstanceManager
- .setParameterlessEngineOrTakePendingWallpaperInteractiveWatchFaceInstance(
- this
- )
+ pendingWallpaperInstance =
+ InteractiveInstanceManager
+ .setParameterlessEngineOrTakePendingWallpaperInteractiveWatchFaceInstance( // ktlint-disable max-line-length
+ this
+ )
}
// If there's a pending WallpaperInteractiveWatchFaceInstance then create it.
@@ -1460,7 +1451,7 @@
@SuppressWarnings("NewApi")
internal fun attachToParameterlessEngine(
pendingWallpaperInstance:
- InteractiveInstanceManager.PendingWallpaperInteractiveWatchFaceInstance
+ InteractiveInstanceManager.PendingWallpaperInteractiveWatchFaceInstance
) {
uiThreadCoroutineScope.launch {
try {
@@ -2241,12 +2232,7 @@
watchState: WatchState
) {
val broadcastsObserver =
- BroadcastsObserver(
- watchState,
- this,
- deferredWatchFaceImpl,
- uiThreadCoroutineScope
- )
+ BroadcastsObserver(watchState, this, deferredWatchFaceImpl, uiThreadCoroutineScope)
// There's no point creating BroadcastsReceiver or listening for Accessibility state
// changes if this is a headless instance.
@@ -2881,7 +2867,6 @@
/**
* If the instance ID for [MutableWatchState.watchFaceInstanceId] begin with this prefix, then the
* system sends consistent IDs for interactive, headless and editor sessions.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
const val SYSTEM_SUPPORTS_CONSISTENT_IDS_PREFIX = "wfId-"
@@ -2889,14 +2874,12 @@
/**
* Instance ID to use when either there's no system id or it doesn't start with
* [SYSTEM_SUPPORTS_CONSISTENT_IDS_PREFIX].
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) const val DEFAULT_INSTANCE_ID = "defaultInstance"
/**
* This is needed to make the instance id consistent between Interactive, Headless and EditorSession
* for old versions of the system.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun sanitizeWatchFaceId(instanceId: String?) =
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt
index b2ef214..29ebfde 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt
@@ -175,6 +175,7 @@
set(@Px value) {
field = value
}
+
public var isHeadless: Boolean = false
public fun asWatchState(): WatchState =
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
index 1dbbc9a..d6e5cdc 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
@@ -73,10 +73,10 @@
}
/**
- * We either return the pendingWallpaperInteractiveWatchFaceInstance if there is one or
- * set parameterlessEngine. A parameterless engine, is one that's been created without any
- * start up params. Typically this can only happen if a WSL watchface is upgraded to an
- * androidx one, so WallpaperManager knows about it but WearServices/WSL does not.
+ * We either return the pendingWallpaperInteractiveWatchFaceInstance if there is one or set
+ * parameterlessEngine. A parameterless engine, is one that's been created without any start
+ * up params. Typically this can only happen if a WSL watchface is upgraded to an androidx
+ * one, so WallpaperManager knows about it but WearServices/WSL does not.
*/
@SuppressLint("SyntheticAccessor")
fun setParameterlessEngineOrTakePendingWallpaperInteractiveWatchFaceInstance(
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/RemoteWatchFaceView.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/RemoteWatchFaceView.kt
index e3abf0a..f2312e1e 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/RemoteWatchFaceView.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/RemoteWatchFaceView.kt
@@ -82,4 +82,4 @@
override fun close() {
host.release()
}
-}
\ No newline at end of file
+}
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
index b41c5d19..92a7cf0 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
@@ -49,10 +49,7 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
-/**
- * A service for creating and controlling watch face instances.
- *
- */
+/** A service for creating and controlling watch face instances. */
@RequiresApi(27)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public open class WatchFaceControlService : Service() {
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/editor/EditorService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/editor/EditorService.kt
index 71b0d50..0755492 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/editor/EditorService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/editor/EditorService.kt
@@ -22,10 +22,7 @@
import androidx.wear.watchface.IndentingPrintWriter
import androidx.wear.watchface.editor.data.EditorStateWireFormat
-/**
- * Implementation of [IEditorService], intended for use by EditorSession only.
- *
- */
+/** Implementation of [IEditorService], intended for use by EditorSession only. */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class EditorService : IEditorService.Stub() {
private val lock = Any()
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
index fa8a2c8..7296865 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
@@ -71,6 +71,7 @@
/** The ids of the [ComplicationSlot]s that have been tapped. */
val tappedComplicationSlotIds: List<Int>
get() = mutableTappedComplicationIds
+
var complicationSelected: Int? = null
var mockZoneId: ZoneId = ZoneId.of("UTC")
var renderer: Renderer? = null
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index e7f10f7..bba42d55 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -23,6 +23,8 @@
import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.ServiceInfo
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
@@ -39,8 +41,6 @@
import android.provider.Settings
import android.support.wearable.complications.ComplicationData as WireComplicationData
import android.support.wearable.complications.ComplicationText as WireComplicationText
-import android.content.IntentFilter
-import android.content.pm.ServiceInfo
import android.support.wearable.watchface.Constants
import android.support.wearable.watchface.IWatchFaceService
import android.support.wearable.watchface.WatchFaceStyle
@@ -3760,21 +3760,22 @@
@Config(sdk = [Build.VERSION_CODES.R])
public fun directBoot() {
val instanceId = "DirectBootInstance"
- val params = WallpaperInteractiveWatchFaceInstanceParams(
- instanceId,
- DeviceConfig(false, false, 0, 0),
- WatchUiState(false, 0),
- UserStyle(
- hashMapOf(
- colorStyleSetting to blueStyleOption,
- watchHandStyleSetting to gothicStyleOption
- )
+ val params =
+ WallpaperInteractiveWatchFaceInstanceParams(
+ instanceId,
+ DeviceConfig(false, false, 0, 0),
+ WatchUiState(false, 0),
+ UserStyle(
+ hashMapOf(
+ colorStyleSetting to blueStyleOption,
+ watchHandStyleSetting to gothicStyleOption
+ )
+ )
+ .toWireFormat(),
+ null,
+ null,
+ null
)
- .toWireFormat(),
- null,
- null,
- null
- )
testWatchFaceService =
TestWatchFaceService(
WatchFaceType.ANALOG,
@@ -5215,19 +5216,19 @@
complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(999))
assertThat(getLeftShortTextComplicationDataText()).isEqualTo("A")
assertThat(
- engineWrapper.contentDescriptionLabels[1]
- .text
- .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
- )
+ engineWrapper.contentDescriptionLabels[1]
+ .text
+ .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
+ )
.isEqualTo("A")
complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(1000))
assertThat(getLeftShortTextComplicationDataText()).isEqualTo("B")
assertThat(
- engineWrapper.contentDescriptionLabels[1]
- .text
- .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
- )
+ engineWrapper.contentDescriptionLabels[1]
+ .text
+ .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
+ )
.isEqualTo("B")
complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(1999))
@@ -5236,10 +5237,10 @@
complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(2000))
assertThat(getLeftShortTextComplicationDataText()).isEqualTo("C")
assertThat(
- engineWrapper.contentDescriptionLabels[1]
- .text
- .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
- )
+ engineWrapper.contentDescriptionLabels[1]
+ .text
+ .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
+ )
.isEqualTo("C")
complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(2999))
@@ -5248,10 +5249,10 @@
complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(3000))
assertThat(getLeftShortTextComplicationDataText()).isEqualTo("B")
assertThat(
- engineWrapper.contentDescriptionLabels[1]
- .text
- .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
- )
+ engineWrapper.contentDescriptionLabels[1]
+ .text
+ .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
+ )
.isEqualTo("B")
complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(3999))
@@ -5260,10 +5261,10 @@
complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(4000))
assertThat(getLeftShortTextComplicationDataText()).isEqualTo("A")
assertThat(
- engineWrapper.contentDescriptionLabels[1]
- .text
- .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
- )
+ engineWrapper.contentDescriptionLabels[1]
+ .text
+ .getTextAt(ApplicationProvider.getApplicationContext<Context>().resources, 0)
+ )
.isEqualTo("A")
}
@@ -6556,13 +6557,14 @@
lateinit var delegate: WatchFace.EditorDelegate
val shadowPackageManager = shadowOf(context.packageManager)
- val controlServiceComponent = ComponentName(
- context, TestWatchFaceControlService::class.java
+ val controlServiceComponent =
+ ComponentName(context, TestWatchFaceControlService::class.java)
+ shadowPackageManager.addOrUpdateService(
+ ServiceInfo().apply {
+ packageName = controlServiceComponent.packageName
+ name = controlServiceComponent.className
+ }
)
- shadowPackageManager.addOrUpdateService(ServiceInfo().apply {
- packageName = controlServiceComponent.packageName
- name = controlServiceComponent.className
- })
shadowPackageManager.addIntentFilterForService(
controlServiceComponent,
IntentFilter(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE)
@@ -6632,19 +6634,20 @@
engineWrapper.onSurfaceChanged(surfaceHolder, 0, 100, 100)
engineWrapper.onVisibilityChanged(true)
- val callback = object : IPendingInteractiveWatchFace.Stub() {
- override fun getApiVersion() = IPendingInteractiveWatchFace.API_VERSION
+ val callback =
+ object : IPendingInteractiveWatchFace.Stub() {
+ override fun getApiVersion() = IPendingInteractiveWatchFace.API_VERSION
- override fun onInteractiveWatchFaceCreated(
- iInteractiveWatchFace: IInteractiveWatchFace
- ) {
- interactiveWatchFaceInstance = iInteractiveWatchFace
- }
+ override fun onInteractiveWatchFaceCreated(
+ iInteractiveWatchFace: IInteractiveWatchFace
+ ) {
+ interactiveWatchFaceInstance = iInteractiveWatchFace
+ }
- override fun onInteractiveWatchFaceCrashed(exception: CrashInfoParcel?) {
- fail("WatchFace crashed: $exception")
+ override fun onInteractiveWatchFaceCrashed(exception: CrashInfoParcel?) {
+ fail("WatchFace crashed: $exception")
+ }
}
- }
InteractiveInstanceManager
.getExistingInstanceOrSetPendingWallpaperInteractiveWatchFaceInstance(
diff --git a/webkit/OWNERS b/webkit/OWNERS
index 3852064..93f399a 100644
--- a/webkit/OWNERS
+++ b/webkit/OWNERS
@@ -1,4 +1,5 @@
# Bug component: 461230
+elabadysayed@google.com
ntfschr@google.com
pbirk@google.com
swestphal@google.com
diff --git a/window/window-testing/api/api_lint.ignore b/window/window-testing/api/api_lint.ignore
index 1789662..f64ca92 100644
--- a/window/window-testing/api/api_lint.ignore
+++ b/window/window-testing/api/api_lint.ignore
@@ -1,9 +1,9 @@
// Baseline format: 1.0
-InvalidNullabilityOverride: androidx.window.testing.layout.StubWindowMetricsCalculatorRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description) parameter #0:
- Invalid nullability on parameter `base` in method `apply`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.window.testing.layout.StubWindowMetricsCalculatorRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description) parameter #1:
- Invalid nullability on parameter `description` in method `apply`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: androidx.window.testing.layout.WindowLayoutInfoPublisherRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description) parameter #0:
Invalid nullability on parameter `base` in method `apply`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: androidx.window.testing.layout.WindowLayoutInfoPublisherRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description) parameter #1:
Invalid nullability on parameter `description` in method `apply`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+InvalidNullabilityOverride: androidx.window.testing.layout.WindowMetricsCalculatorRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description) parameter #0:
+ Invalid nullability on parameter `base` in method `apply`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+InvalidNullabilityOverride: androidx.window.testing.layout.WindowMetricsCalculatorRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description) parameter #1:
+ Invalid nullability on parameter `description` in method `apply`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
diff --git a/window/window-testing/api/current.txt b/window/window-testing/api/current.txt
index 12a7d20..28a4315 100644
--- a/window/window-testing/api/current.txt
+++ b/window/window-testing/api/current.txt
@@ -1,4 +1,38 @@
// Signature format: 4.0
+package androidx.window.testing.embedding {
+
+ public final class ActivityEmbeddingRule implements org.junit.rules.TestRule {
+ ctor public ActivityEmbeddingRule();
+ method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+ method public void overrideIsActivityEmbedded(android.app.Activity activity, boolean isActivityEmbedded);
+ method public void overrideSplitInfo(android.app.Activity activity, java.util.List<androidx.window.embedding.SplitInfo> splitInfoList);
+ method public void overrideSplitSupportStatus(androidx.window.embedding.SplitController.SplitSupportStatus status);
+ }
+
+ public final class TestActivityStack {
+ method public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess, optional boolean isEmpty);
+ method public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess);
+ method public static androidx.window.embedding.ActivityStack createTestActivityStack();
+ }
+
+ public final class TestSplitAttributesCalculatorParams {
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied, optional String? splitRuleTag);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics);
+ }
+
+ public final class TestSplitInfo {
+ method public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack, optional androidx.window.embedding.SplitAttributes splitAttributes);
+ method public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack);
+ method public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack);
+ method public static androidx.window.embedding.SplitInfo createTestSplitInfo();
+ }
+
+}
+
package androidx.window.testing.layout {
public final class DisplayFeatureTesting {
@@ -7,6 +41,11 @@
method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+ method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+ method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+ method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+ method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+ method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
}
public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
@@ -20,5 +59,10 @@
method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
}
+ public final class WindowMetricsCalculatorRule implements org.junit.rules.TestRule {
+ ctor public WindowMetricsCalculatorRule();
+ method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+ }
+
}
diff --git a/window/window-testing/api/public_plus_experimental_current.txt b/window/window-testing/api/public_plus_experimental_current.txt
index 16174f8..28a4315 100644
--- a/window/window-testing/api/public_plus_experimental_current.txt
+++ b/window/window-testing/api/public_plus_experimental_current.txt
@@ -1,8 +1,8 @@
// Signature format: 4.0
package androidx.window.testing.embedding {
- @androidx.window.core.ExperimentalWindowApi public final class ActivityEmbeddingTestRule implements org.junit.rules.TestRule {
- ctor public ActivityEmbeddingTestRule();
+ public final class ActivityEmbeddingRule implements org.junit.rules.TestRule {
+ ctor public ActivityEmbeddingRule();
method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
method public void overrideIsActivityEmbedded(android.app.Activity activity, boolean isActivityEmbedded);
method public void overrideSplitInfo(android.app.Activity activity, java.util.List<androidx.window.embedding.SplitInfo> splitInfoList);
@@ -10,25 +10,25 @@
}
public final class TestActivityStack {
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess, optional boolean isEmpty);
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess);
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack();
+ method public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess, optional boolean isEmpty);
+ method public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess);
+ method public static androidx.window.embedding.ActivityStack createTestActivityStack();
}
public final class TestSplitAttributesCalculatorParams {
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied, optional String? splitRuleTag);
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied);
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes);
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo);
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration);
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied, optional String? splitRuleTag);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics);
}
public final class TestSplitInfo {
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack, optional androidx.window.embedding.SplitAttributes splitAttributes);
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack);
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack);
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo();
+ method public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack, optional androidx.window.embedding.SplitAttributes splitAttributes);
+ method public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack);
+ method public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack);
+ method public static androidx.window.embedding.SplitInfo createTestSplitInfo();
}
}
@@ -41,16 +41,11 @@
method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
- method @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
- }
-
- @androidx.window.core.ExperimentalWindowApi public final class StubWindowMetricsCalculatorRule implements org.junit.rules.TestRule {
- ctor public StubWindowMetricsCalculatorRule();
- method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+ method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+ method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+ method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+ method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+ method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
}
public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
@@ -64,5 +59,10 @@
method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
}
+ public final class WindowMetricsCalculatorRule implements org.junit.rules.TestRule {
+ ctor public WindowMetricsCalculatorRule();
+ method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+ }
+
}
diff --git a/window/window-testing/api/restricted_current.txt b/window/window-testing/api/restricted_current.txt
index 12a7d20..28a4315 100644
--- a/window/window-testing/api/restricted_current.txt
+++ b/window/window-testing/api/restricted_current.txt
@@ -1,4 +1,38 @@
// Signature format: 4.0
+package androidx.window.testing.embedding {
+
+ public final class ActivityEmbeddingRule implements org.junit.rules.TestRule {
+ ctor public ActivityEmbeddingRule();
+ method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+ method public void overrideIsActivityEmbedded(android.app.Activity activity, boolean isActivityEmbedded);
+ method public void overrideSplitInfo(android.app.Activity activity, java.util.List<androidx.window.embedding.SplitInfo> splitInfoList);
+ method public void overrideSplitSupportStatus(androidx.window.embedding.SplitController.SplitSupportStatus status);
+ }
+
+ public final class TestActivityStack {
+ method public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess, optional boolean isEmpty);
+ method public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess);
+ method public static androidx.window.embedding.ActivityStack createTestActivityStack();
+ }
+
+ public final class TestSplitAttributesCalculatorParams {
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied, optional String? splitRuleTag);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration);
+ method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics);
+ }
+
+ public final class TestSplitInfo {
+ method public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack, optional androidx.window.embedding.SplitAttributes splitAttributes);
+ method public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack);
+ method public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack);
+ method public static androidx.window.embedding.SplitInfo createTestSplitInfo();
+ }
+
+}
+
package androidx.window.testing.layout {
public final class DisplayFeatureTesting {
@@ -7,6 +41,11 @@
method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+ method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+ method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+ method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+ method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+ method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
}
public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
@@ -20,5 +59,10 @@
method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
}
+ public final class WindowMetricsCalculatorRule implements org.junit.rules.TestRule {
+ ctor public WindowMetricsCalculatorRule();
+ method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+ }
+
}
diff --git a/window/window-testing/src/androidTest/java/androidx/window/testing/layout/StubWindowMetricsCalculatorRuleTest.kt b/window/window-testing/src/androidTest/java/androidx/window/testing/layout/WindowMetricsCalculatorRuleTest.kt
similarity index 94%
rename from window/window-testing/src/androidTest/java/androidx/window/testing/layout/StubWindowMetricsCalculatorRuleTest.kt
rename to window/window-testing/src/androidTest/java/androidx/window/testing/layout/WindowMetricsCalculatorRuleTest.kt
index 3e6544d..a663eaf 100644
--- a/window/window-testing/src/androidTest/java/androidx/window/testing/layout/StubWindowMetricsCalculatorRuleTest.kt
+++ b/window/window-testing/src/androidTest/java/androidx/window/testing/layout/WindowMetricsCalculatorRuleTest.kt
@@ -23,7 +23,6 @@
import androidx.annotation.RequiresApi
import androidx.test.core.app.ActivityScenario
import androidx.test.ext.junit.rules.ActivityScenarioRule
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.layout.WindowMetricsCalculator
import androidx.window.testing.TestActivity
import org.junit.Assert.assertEquals
@@ -35,19 +34,18 @@
import org.junit.runners.model.Statement
/**
- * A test class for [StubWindowMetricsCalculatorRule] that tests using
+ * A test class for [WindowMetricsCalculatorRule] that tests using
* [StubWindowMetricsCalculator] instead of the actual implementation.
*/
-@OptIn(ExperimentalWindowApi::class)
-class StubWindowMetricsCalculatorRuleTest {
+class WindowMetricsCalculatorRuleTest {
private val activityRule = ActivityScenarioRule(TestActivity::class.java)
- private val stubWindowMetricsCalculatorRule = StubWindowMetricsCalculatorRule()
+ private val mWindowMetricsCalculatorRule = WindowMetricsCalculatorRule()
@get:Rule
val testRule: TestRule
init {
- testRule = RuleChain.outerRule(stubWindowMetricsCalculatorRule).around(activityRule)
+ testRule = RuleChain.outerRule(mWindowMetricsCalculatorRule).around(activityRule)
}
@Test
@@ -166,7 +164,7 @@
WindowMetricsCalculator.reset()
val expected = WindowMetricsCalculator.getOrCreate()
try {
- StubWindowMetricsCalculatorRule().apply(
+ WindowMetricsCalculatorRule().apply(
object : Statement() {
override fun evaluate() {
throw TestException
diff --git a/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityEmbeddingTestRule.kt b/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityEmbeddingRule.kt
similarity index 96%
rename from window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityEmbeddingTestRule.kt
rename to window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityEmbeddingRule.kt
index 70d10c41..cd9fe23 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityEmbeddingTestRule.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityEmbeddingRule.kt
@@ -17,7 +17,6 @@
package androidx.window.testing.embedding
import android.app.Activity
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.embedding.ActivityEmbeddingController
import androidx.window.embedding.EmbeddingBackend
import androidx.window.embedding.RuleController
@@ -33,8 +32,7 @@
* [RuleController] with a more simple one that will support testing independent of the current
* platform.
*/
-@ExperimentalWindowApi
-class ActivityEmbeddingTestRule : TestRule {
+class ActivityEmbeddingRule : TestRule {
private val stubEmbeddingBackend = StubEmbeddingBackend()
private val decorator = StubEmbeddingBackendDecorator(stubEmbeddingBackend)
diff --git a/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityStackTesting.kt b/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityStackTesting.kt
index a8e66a2..3bbca06 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityStackTesting.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityStackTesting.kt
@@ -18,7 +18,6 @@
package androidx.window.testing.embedding
import android.app.Activity
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.embedding.ActivityStack
/**
@@ -35,7 +34,6 @@
* @return An [ActivityStack] instance for testing
*/
@Suppress("FunctionName")
-@ExperimentalWindowApi
@JvmName("createTestActivityStack")
@JvmOverloads
fun TestActivityStack(
diff --git a/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTesting.kt b/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTesting.kt
index bf20738..dd17311 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTesting.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTesting.kt
@@ -55,7 +55,7 @@
*
* @see SplitAttributesCalculatorParams
*/
-@ExperimentalWindowApi
+@OptIn(ExperimentalWindowApi::class)
@Suppress("FunctionName")
@JvmName("createTestSplitAttributesCalculatorParams")
@JvmOverloads
diff --git a/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitInfoTesting.kt b/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitInfoTesting.kt
index 3ce87ed..e3d3ac9 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitInfoTesting.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitInfoTesting.kt
@@ -17,7 +17,6 @@
package androidx.window.testing.embedding
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.embedding.ActivityStack
import androidx.window.embedding.SplitAttributes
import androidx.window.embedding.SplitInfo
@@ -37,7 +36,6 @@
* @return A [SplitInfo] instance for testing
*/
@Suppress("FunctionName")
-@ExperimentalWindowApi
@JvmName("createTestSplitInfo")
@JvmOverloads
fun TestSplitInfo(
diff --git a/window/window-testing/src/main/java/androidx/window/testing/embedding/StubEmbeddingBackendDecorator.kt b/window/window-testing/src/main/java/androidx/window/testing/embedding/StubEmbeddingBackendDecorator.kt
index 2f0b90d..94bbc1a 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/embedding/StubEmbeddingBackendDecorator.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/embedding/StubEmbeddingBackendDecorator.kt
@@ -16,14 +16,12 @@
package androidx.window.testing.embedding
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.embedding.EmbeddingBackend
import androidx.window.embedding.EmbeddingBackendDecorator
/**
* Decorator to return [StubEmbeddingBackend] instead of the actual implementation.
*/
-@ExperimentalWindowApi
internal class StubEmbeddingBackendDecorator(
private val stubEmbeddingBackend: StubEmbeddingBackend
) : EmbeddingBackendDecorator {
diff --git a/window/window-testing/src/main/java/androidx/window/testing/layout/DisplayFeatureTesting.kt b/window/window-testing/src/main/java/androidx/window/testing/layout/DisplayFeatureTesting.kt
index b6aa95e..2a065cb 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/layout/DisplayFeatureTesting.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/layout/DisplayFeatureTesting.kt
@@ -19,7 +19,6 @@
import android.app.Activity
import android.graphics.Rect
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.layout.FoldingFeature
import androidx.window.layout.FoldingFeature.OcclusionType.Companion.FULL
import androidx.window.layout.FoldingFeature.OcclusionType.Companion.NONE
@@ -96,7 +95,6 @@
*/
@JvmOverloads
@JvmName("createFoldingFeature")
-@ExperimentalWindowApi
fun FoldingFeature(
windowBounds: Rect,
center: Int = -1,
diff --git a/window/window-testing/src/main/java/androidx/window/testing/layout/StubMetricDecorator.kt b/window/window-testing/src/main/java/androidx/window/testing/layout/StubMetricDecorator.kt
index 14bf0c7..7a8eeb2 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/layout/StubMetricDecorator.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/layout/StubMetricDecorator.kt
@@ -16,14 +16,12 @@
package androidx.window.testing.layout
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.layout.WindowMetricsCalculator
import androidx.window.layout.WindowMetricsCalculatorDecorator
/**
* A decorator to return [StubWindowMetricsCalculator] instead of the actual implementation.
*/
-@ExperimentalWindowApi
internal object StubMetricDecorator : WindowMetricsCalculatorDecorator {
override fun decorate(calculator: WindowMetricsCalculator): WindowMetricsCalculator {
return StubWindowMetricsCalculator()
diff --git a/window/window-testing/src/main/java/androidx/window/testing/layout/WindowLayoutInfoPublisherRule.kt b/window/window-testing/src/main/java/androidx/window/testing/layout/WindowLayoutInfoPublisherRule.kt
index c9c7aeb..b8d4b070 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/layout/WindowLayoutInfoPublisherRule.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/layout/WindowLayoutInfoPublisherRule.kt
@@ -45,7 +45,7 @@
* <li>A fold in the middle and has horizontal orientation.</li>
* </ul>
*/
-public class WindowLayoutInfoPublisherRule() : TestRule {
+class WindowLayoutInfoPublisherRule : TestRule {
private val flow = MutableSharedFlow<WindowLayoutInfo>(
extraBufferCapacity = 1,
@@ -70,7 +70,7 @@
* Send an arbitrary [WindowLayoutInfo] through
* [androidx.window.layout.WindowInfoTracker.windowLayoutInfo]. Each event is sent only once.
*/
- public fun overrideWindowLayoutInfo(info: WindowLayoutInfo) {
+ fun overrideWindowLayoutInfo(info: WindowLayoutInfo) {
flow.tryEmit(info)
}
}
\ No newline at end of file
diff --git a/window/window-testing/src/main/java/androidx/window/testing/layout/StubWindowMetricsCalculatorRule.kt b/window/window-testing/src/main/java/androidx/window/testing/layout/WindowMetricsCalculatorRule.kt
similarity index 93%
rename from window/window-testing/src/main/java/androidx/window/testing/layout/StubWindowMetricsCalculatorRule.kt
rename to window/window-testing/src/main/java/androidx/window/testing/layout/WindowMetricsCalculatorRule.kt
index bfb7c48..10e68f1 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/layout/StubWindowMetricsCalculatorRule.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/layout/WindowMetricsCalculatorRule.kt
@@ -17,7 +17,6 @@
package androidx.window.testing.layout
import android.app.Activity
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.layout.WindowMetricsCalculator
import org.junit.rules.TestRule
import org.junit.runner.Description
@@ -32,8 +31,7 @@
* the Espresso Test framework with an actual [Activity] and use the actual
* [WindowMetricsCalculator].
*/
-@ExperimentalWindowApi
-class StubWindowMetricsCalculatorRule : TestRule {
+class WindowMetricsCalculatorRule : TestRule {
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
diff --git a/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityEmbeddingTestRuleTest.kt b/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityEmbeddingRuleTest.kt
similarity index 96%
rename from window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityEmbeddingTestRuleTest.kt
rename to window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityEmbeddingRuleTest.kt
index b1c22cb..6bd7721 100644
--- a/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityEmbeddingTestRuleTest.kt
+++ b/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityEmbeddingRuleTest.kt
@@ -19,7 +19,6 @@
import android.app.Activity
import android.app.Application
import android.content.Intent
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.embedding.ActivityEmbeddingController
import androidx.window.embedding.ActivityRule
import androidx.window.embedding.EmbeddingBackend
@@ -51,12 +50,12 @@
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
-/** Test for [ActivityEmbeddingTestRule]. */
-@OptIn(ExperimentalWindowApi::class, ExperimentalCoroutinesApi::class)
-class ActivityEmbeddingTestRuleTest {
+/** Test for [ActivityEmbeddingRule]. */
+@OptIn(ExperimentalCoroutinesApi::class)
+class ActivityEmbeddingRuleTest {
@get:Rule
- val testRule: ActivityEmbeddingTestRule = ActivityEmbeddingTestRule()
+ val testRule: ActivityEmbeddingRule = ActivityEmbeddingRule()
private val mockActivity: Activity = mock()
private val testScope = TestScope()
@@ -246,7 +245,7 @@
fun testRuleResetsOnException() {
EmbeddingBackend.reset()
try {
- ActivityEmbeddingTestRule().apply(
+ ActivityEmbeddingRule().apply(
object : Statement() {
override fun evaluate() {
throw TestException
diff --git a/window/window/api/current.txt b/window/window/api/current.txt
index 5617dbb..8fbbb45 100644
--- a/window/window/api/current.txt
+++ b/window/window/api/current.txt
@@ -302,6 +302,7 @@
public interface WindowInfoTracker {
method public default static androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+ method public default kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(@UiContext android.content.Context context);
method public kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(android.app.Activity activity);
field public static final androidx.window.layout.WindowInfoTracker.Companion Companion;
}
diff --git a/window/window/api/public_plus_experimental_current.txt b/window/window/api/public_plus_experimental_current.txt
index 0ac0175..0c1d735 100644
--- a/window/window/api/public_plus_experimental_current.txt
+++ b/window/window/api/public_plus_experimental_current.txt
@@ -330,7 +330,7 @@
public interface WindowInfoTracker {
method public default static androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
- method @androidx.window.core.ExperimentalWindowApi public default kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(@UiContext android.content.Context context);
+ method public default kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(@UiContext android.content.Context context);
method public kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(android.app.Activity activity);
field public static final androidx.window.layout.WindowInfoTracker.Companion Companion;
}
diff --git a/window/window/api/restricted_current.txt b/window/window/api/restricted_current.txt
index 5617dbb..8fbbb45 100644
--- a/window/window/api/restricted_current.txt
+++ b/window/window/api/restricted_current.txt
@@ -302,6 +302,7 @@
public interface WindowInfoTracker {
method public default static androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+ method public default kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(@UiContext android.content.Context context);
method public kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(android.app.Activity activity);
field public static final androidx.window.layout.WindowInfoTracker.Companion Companion;
}
diff --git a/window/window/src/androidTest/java/androidx/window/embedding/SafeActivityEmbeddingComponentProviderTest.kt b/window/window/src/androidTest/java/androidx/window/embedding/SafeActivityEmbeddingComponentProviderTest.kt
index 3b93a9a..1eab9d0 100644
--- a/window/window/src/androidTest/java/androidx/window/embedding/SafeActivityEmbeddingComponentProviderTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/embedding/SafeActivityEmbeddingComponentProviderTest.kt
@@ -18,9 +18,13 @@
import android.util.Log
import androidx.window.core.ConsumerAdapter
+import androidx.window.core.ExtensionsUtil
import androidx.window.extensions.WindowExtensions
+import androidx.window.extensions.WindowExtensions.VENDOR_API_LEVEL_1
import androidx.window.extensions.WindowExtensionsProvider
+import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
import org.junit.Test
/**
@@ -31,14 +35,13 @@
*/
class SafeActivityEmbeddingComponentProviderTest {
+ // TODO(b/267708462) : add a more reliable test
/**
* Test that if [WindowExtensionsProvider] is available then
* use [SafeActivityEmbeddingComponentProvider.activityEmbeddingComponent] to validate.
* If [WindowExtensions.getActivityEmbeddingComponent] matches contract,
* return a non-null value.
* If it doesn't match, it will return a null.
- *
- * TODO(b/267708462) : add a more reliable test
*/
@Test
fun activityEmbeddingComponentIsAvailable_ifProviderIsAvailable() {
@@ -50,12 +53,12 @@
Log.d(TAG, "Device doesn't have WindowExtensions available")
return
}
- val safeComponent = SafeActivityEmbeddingComponentProvider(
+ val safeProvider = SafeActivityEmbeddingComponentProvider(
loader,
consumerAdapter,
windowExtensions
)
- .activityEmbeddingComponent
+ val safeComponent = safeProvider.activityEmbeddingComponent
try {
val actualComponent = windowExtensions.activityEmbeddingComponent
if (actualComponent == null) {
@@ -63,8 +66,11 @@
} else {
// TODO(b/267573854) : verify upon each api level
// TODO(b/267708462) : more reliable test for testing actual method matching
- if (safeComponent == null) {
- Log.d(TAG, "ActivityEmbeddingComponent on device doesn't match our constraints")
+ assertNotNull(safeComponent)
+ assertTrue(safeProvider.isActivityEmbeddingComponentAccessible())
+ when (ExtensionsUtil.safeVendorApiLevel) {
+ VENDOR_API_LEVEL_1 -> assertTrue(safeProvider.hasValidVendorApiLevel1())
+ else -> assertTrue(safeProvider.hasValidVendorApiLevel2())
}
}
} catch (e: UnsupportedOperationException) {
diff --git a/window/window/src/androidTest/java/androidx/window/layout/SafeWindowLayoutComponentProviderTest.kt b/window/window/src/androidTest/java/androidx/window/layout/SafeWindowLayoutComponentProviderTest.kt
index bc78a38..9855ce8 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/SafeWindowLayoutComponentProviderTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/SafeWindowLayoutComponentProviderTest.kt
@@ -16,10 +16,13 @@
package androidx.window.layout
-import android.util.Log
import androidx.window.core.ConsumerAdapter
+import androidx.window.core.ExtensionsUtil
+import androidx.window.extensions.WindowExtensions.VENDOR_API_LEVEL_1
import androidx.window.extensions.WindowExtensionsProvider
+import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
import org.junit.Test
/**
@@ -37,8 +40,8 @@
fun windowLayoutComponentIsAvailable_ifProviderIsAvailable() {
val loader = SafeWindowLayoutComponentProviderTest::class.java.classLoader!!
val consumerAdapter = ConsumerAdapter(loader)
- val safeComponent = SafeWindowLayoutComponentProvider(loader, consumerAdapter)
- .windowLayoutComponent
+ val safeProvider = SafeWindowLayoutComponentProvider(loader, consumerAdapter)
+ val safeComponent = safeProvider.windowLayoutComponent
try {
val extensions = WindowExtensionsProvider.getWindowExtensions()
@@ -48,15 +51,16 @@
} else {
// TODO(b/267831038): verify upon each api level
// TODO(b/267708462): more reliable test for testing actual method matching
- Log.d(TAG, "WindowLayoutComponent on device doesn't match our constraints")
+ assertNotNull(safeComponent)
+ assertTrue(safeProvider.isWindowLayoutComponentAccessible())
+ when (ExtensionsUtil.safeVendorApiLevel) {
+ VENDOR_API_LEVEL_1 -> assertTrue(safeProvider.hasValidVendorApiLevel1())
+ else -> assertTrue(safeProvider.hasValidVendorApiLevel2())
+ }
}
} catch (e: UnsupportedOperationException) {
// Invalid implementation of extensions
assertNull(safeComponent)
}
}
-
- companion object {
- private const val TAG = "SafeWindowLayoutComponentProviderTest"
- }
}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/SafeWindowExtensionsProvider.kt b/window/window/src/main/java/androidx/window/SafeWindowExtensionsProvider.kt
new file mode 100644
index 0000000..2aa82f4
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/SafeWindowExtensionsProvider.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.window
+
+import androidx.window.reflection.ReflectionUtils
+import androidx.window.reflection.ReflectionUtils.doesReturn
+import androidx.window.reflection.ReflectionUtils.isPublic
+import androidx.window.reflection.WindowExtensionsConstants
+
+internal class SafeWindowExtensionsProvider(private val loader: ClassLoader) {
+ internal val windowExtensionsClass: Class<*>
+ get() {
+ return loader.loadClass(WindowExtensionsConstants.WINDOW_EXTENSIONS_CLASS)
+ }
+
+ internal fun isWindowExtensionsValid(): Boolean {
+ return isWindowExtensionsPresent() &&
+ ReflectionUtils.validateReflection(
+ "WindowExtensionsProvider#getWindowExtensions is not valid"
+ ) {
+ val providerClass = windowExtensionsProviderClass
+ val getWindowExtensionsMethod = providerClass
+ .getDeclaredMethod("getWindowExtensions")
+ val windowExtensionsClass = windowExtensionsClass
+ getWindowExtensionsMethod.doesReturn(windowExtensionsClass) &&
+ getWindowExtensionsMethod.isPublic
+ }
+ }
+
+ private fun isWindowExtensionsPresent(): Boolean {
+ return ReflectionUtils.checkIsPresent {
+ loader.loadClass(WindowExtensionsConstants.WINDOW_EXTENSIONS_PROVIDER_CLASS)
+ }
+ }
+ private val windowExtensionsProviderClass: Class<*>
+ get() {
+ return loader.loadClass(WindowExtensionsConstants.WINDOW_EXTENSIONS_PROVIDER_CLASS)
+ }
+}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/embedding/EmbeddingBackend.kt b/window/window/src/main/java/androidx/window/embedding/EmbeddingBackend.kt
index 117337d..02b8a67 100644
--- a/window/window/src/main/java/androidx/window/embedding/EmbeddingBackend.kt
+++ b/window/window/src/main/java/androidx/window/embedding/EmbeddingBackend.kt
@@ -70,14 +70,12 @@
return decorator(ExtensionEmbeddingBackend.getInstance(context))
}
- @ExperimentalWindowApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@JvmStatic
fun overrideDecorator(overridingDecorator: EmbeddingBackendDecorator) {
decorator = overridingDecorator::decorate
}
- @ExperimentalWindowApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@JvmStatic
fun reset() {
@@ -86,7 +84,6 @@
}
}
-@ExperimentalWindowApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
interface EmbeddingBackendDecorator {
diff --git a/window/window/src/main/java/androidx/window/embedding/SafeActivityEmbeddingComponentProvider.kt b/window/window/src/main/java/androidx/window/embedding/SafeActivityEmbeddingComponentProvider.kt
index 44699d8..92965c3 100644
--- a/window/window/src/main/java/androidx/window/embedding/SafeActivityEmbeddingComponentProvider.kt
+++ b/window/window/src/main/java/androidx/window/embedding/SafeActivityEmbeddingComponentProvider.kt
@@ -17,6 +17,7 @@
package androidx.window.embedding
import android.app.Activity
+import androidx.annotation.VisibleForTesting
import androidx.window.core.ConsumerAdapter
import androidx.window.core.ExtensionsUtil
import androidx.window.extensions.WindowExtensions
@@ -26,9 +27,8 @@
import androidx.window.reflection.ReflectionUtils.doesReturn
import androidx.window.reflection.ReflectionUtils.isPublic
import androidx.window.reflection.ReflectionUtils.validateReflection
+import androidx.window.SafeWindowExtensionsProvider
import androidx.window.reflection.WindowExtensionsConstants.ACTIVITY_EMBEDDING_COMPONENT_CLASS
-import androidx.window.reflection.WindowExtensionsConstants.WINDOW_EXTENSIONS_CLASS
-import androidx.window.reflection.WindowExtensionsConstants.WINDOW_EXTENSIONS_PROVIDER_CLASS
/**
* Reflection Guard for [ActivityEmbeddingComponent].
@@ -40,6 +40,8 @@
private val consumerAdapter: ConsumerAdapter,
private val windowExtensions: WindowExtensions
) {
+ private val safeWindowExtensionsProvider = SafeWindowExtensionsProvider(loader)
+
val activityEmbeddingComponent: ActivityEmbeddingComponent?
get() {
return if (canUseActivityEmbeddingComponent()) {
@@ -54,7 +56,7 @@
}
private fun canUseActivityEmbeddingComponent(): Boolean {
- if (!isWindowExtensionsValid() || !isActivityEmbeddingComponentValid()) {
+ if (!isActivityEmbeddingComponentAccessible()) {
return false
}
// TODO(b/267573854) : update logic to fallback to lower version
@@ -67,6 +69,11 @@
}
}
+ @VisibleForTesting
+ internal fun isActivityEmbeddingComponentAccessible(): Boolean =
+ safeWindowExtensionsProvider.isWindowExtensionsValid() &&
+ isActivityEmbeddingComponentValid()
+
/**
* [WindowExtensions.VENDOR_API_LEVEL_1] includes the following methods:
* - [ActivityEmbeddingComponent.setEmbeddingRules]
@@ -78,7 +85,8 @@
* - [androidx.window.extensions.embedding.SplitPlaceholderRule]
* - [androidx.window.extensions.embedding.SplitInfo]
*/
- private fun hasValidVendorApiLevel1(): Boolean {
+ @VisibleForTesting
+ internal fun hasValidVendorApiLevel1(): Boolean {
return isMethodSetEmbeddingRulesValid() &&
isMethodIsActivityEmbeddedValid() &&
isMethodSetSplitInfoCallbackJavaConsumerValid()
@@ -93,7 +101,8 @@
* and following classes: TODO(b/268583307) : add guard function for those classes
* - [androidx.window.extensions.embedding.SplitAttributes]
*/
- private fun hasValidVendorApiLevel2(): Boolean {
+ @VisibleForTesting
+ internal fun hasValidVendorApiLevel2(): Boolean {
return hasValidVendorApiLevel1() &&
isMethodSetSplitInfoCallbackWindowConsumerValid() &&
isMethodClearSplitInfoCallbackValid() &&
@@ -166,20 +175,9 @@
}
}
- private fun isWindowExtensionsValid(): Boolean {
- return validateReflection("WindowExtensionsProvider#getWindowExtensions is not valid") {
- val providerClass = windowExtensionsProviderClass
- val getWindowExtensionsMethod = providerClass.getDeclaredMethod("getWindowExtensions")
- val windowExtensionsClass = windowExtensionsClass
- getWindowExtensionsMethod.isPublic && getWindowExtensionsMethod.doesReturn(
- windowExtensionsClass
- )
- }
- }
-
private fun isActivityEmbeddingComponentValid(): Boolean {
return validateReflection("WindowExtensions#getActivityEmbeddingComponent is not valid") {
- val extensionsClass = windowExtensionsClass
+ val extensionsClass = safeWindowExtensionsProvider.windowExtensionsClass
val getActivityEmbeddingComponentMethod =
extensionsClass.getMethod("getActivityEmbeddingComponent")
val activityEmbeddingComponentClass = activityEmbeddingComponentClass
@@ -188,16 +186,6 @@
}
}
- private val windowExtensionsProviderClass: Class<*>
- get() {
- return loader.loadClass(WINDOW_EXTENSIONS_PROVIDER_CLASS)
- }
-
- private val windowExtensionsClass: Class<*>
- get() {
- return loader.loadClass(WINDOW_EXTENSIONS_CLASS)
- }
-
private val activityEmbeddingComponentClass: Class<*>
get() {
return loader.loadClass(ACTIVITY_EMBEDDING_COMPONENT_CLASS)
diff --git a/window/window/src/main/java/androidx/window/layout/SafeWindowLayoutComponentProvider.kt b/window/window/src/main/java/androidx/window/layout/SafeWindowLayoutComponentProvider.kt
index bc0dac5..b9dcbef 100644
--- a/window/window/src/main/java/androidx/window/layout/SafeWindowLayoutComponentProvider.kt
+++ b/window/window/src/main/java/androidx/window/layout/SafeWindowLayoutComponentProvider.kt
@@ -19,21 +19,20 @@
import android.app.Activity
import android.content.Context
import android.graphics.Rect
+import androidx.annotation.VisibleForTesting
import androidx.window.core.ConsumerAdapter
import androidx.window.core.ExtensionsUtil
import androidx.window.extensions.WindowExtensions
import androidx.window.extensions.WindowExtensionsProvider
import androidx.window.extensions.core.util.function.Consumer
import androidx.window.extensions.layout.WindowLayoutComponent
-import androidx.window.reflection.ReflectionUtils.checkIsPresent
import androidx.window.reflection.ReflectionUtils.doesReturn
import androidx.window.reflection.ReflectionUtils.isPublic
import androidx.window.reflection.ReflectionUtils.validateReflection
+import androidx.window.SafeWindowExtensionsProvider
import androidx.window.reflection.WindowExtensionsConstants.FOLDING_FEATURE_CLASS
import androidx.window.reflection.WindowExtensionsConstants.JAVA_CONSUMER
import androidx.window.reflection.WindowExtensionsConstants.WINDOW_CONSUMER
-import androidx.window.reflection.WindowExtensionsConstants.WINDOW_EXTENSIONS_CLASS
-import androidx.window.reflection.WindowExtensionsConstants.WINDOW_EXTENSIONS_PROVIDER_CLASS
import androidx.window.reflection.WindowExtensionsConstants.WINDOW_LAYOUT_COMPONENT_CLASS
/**
@@ -45,6 +44,7 @@
private val loader: ClassLoader,
private val consumerAdapter: ConsumerAdapter
) {
+ private val safeWindowExtensionsProvider = SafeWindowExtensionsProvider(loader)
val windowLayoutComponent: WindowLayoutComponent?
get() {
@@ -60,10 +60,7 @@
}
private fun canUseWindowLayoutComponent(): Boolean {
- if (!isWindowExtensionsPresent() || !isWindowExtensionsValid() ||
- !isWindowLayoutProviderValid() ||
- !isFoldingFeatureValid()
- ) {
+ if (!isWindowLayoutComponentAccessible()) {
return false
}
// TODO(b/267831038): can fallback to VendorApiLevel1 when level2 is not match
@@ -76,11 +73,11 @@
}
}
- private fun isWindowExtensionsPresent(): Boolean {
- return checkIsPresent {
- loader.loadClass(WINDOW_EXTENSIONS_PROVIDER_CLASS)
- }
- }
+ @VisibleForTesting
+ internal fun isWindowLayoutComponentAccessible(): Boolean =
+ safeWindowExtensionsProvider.isWindowExtensionsValid() &&
+ isWindowLayoutProviderValid() &&
+ isFoldingFeatureValid()
/**
* [WindowExtensions.VENDOR_API_LEVEL_1] includes the following methods
@@ -88,35 +85,24 @@
* [java.util.function.Consumer]
* - [WindowLayoutComponent.removeWindowLayoutInfoListener] with [java.util.function.Consumer]
*/
- private fun hasValidVendorApiLevel1(): Boolean {
+ @VisibleForTesting
+ internal fun hasValidVendorApiLevel1(): Boolean {
return isMethodWindowLayoutInfoListenerJavaConsumerValid()
}
/**
* [WindowExtensions.VENDOR_API_LEVEL_2] includes the following methods
- * - [WindowLayoutComponent.addWindowLayoutInfoListener] with [Context] and
- * [java.util.function.Consumer]
* - [WindowLayoutComponent.addWindowLayoutInfoListener] with [Context] and [Consumer]
* - [WindowLayoutComponent.removeWindowLayoutInfoListener] with [Consumer]
*/
- private fun hasValidVendorApiLevel2(): Boolean {
- return hasValidVendorApiLevel1() &&
- isMethodWindowLayoutInfoListenerWindowConsumerValid()
- }
-
- private fun isWindowExtensionsValid(): Boolean {
- return validateReflection("WindowExtensionsProvider#getWindowExtensions is not valid") {
- val providerClass = windowExtensionsProviderClass
- val getWindowExtensionsMethod = providerClass.getDeclaredMethod("getWindowExtensions")
- val windowExtensionsClass = windowExtensionsClass
- getWindowExtensionsMethod.doesReturn(windowExtensionsClass) &&
- getWindowExtensionsMethod.isPublic
- }
+ @VisibleForTesting
+ internal fun hasValidVendorApiLevel2(): Boolean {
+ return hasValidVendorApiLevel1() && isMethodWindowLayoutInfoListenerWindowConsumerValid()
}
private fun isWindowLayoutProviderValid(): Boolean {
return validateReflection("WindowExtensions#getWindowLayoutComponent is not valid") {
- val extensionsClass = windowExtensionsClass
+ val extensionsClass = safeWindowExtensionsProvider.windowExtensionsClass
val getWindowLayoutComponentMethod =
extensionsClass.getMethod("getWindowLayoutComponent")
val windowLayoutComponentClass = windowLayoutComponentClass
@@ -178,16 +164,6 @@
}
}
- private val windowExtensionsProviderClass: Class<*>
- get() {
- return loader.loadClass(WINDOW_EXTENSIONS_PROVIDER_CLASS)
- }
-
- private val windowExtensionsClass: Class<*>
- get() {
- return loader.loadClass(WINDOW_EXTENSIONS_CLASS)
- }
-
private val foldingFeatureClass: Class<*>
get() {
return loader.loadClass(FOLDING_FEATURE_CLASS)
diff --git a/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt b/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt
index 8af11e0..7ecff91 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt
@@ -24,7 +24,6 @@
import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
import androidx.annotation.UiContext
import androidx.window.core.ConsumerAdapter
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.layout.adapter.WindowBackend
import androidx.window.layout.adapter.extensions.ExtensionWindowLayoutInfoBackend
import androidx.window.layout.adapter.sidecar.SidecarWindowBackend
@@ -62,9 +61,9 @@
* @throws NotImplementedError when [Context] is not an [UiContext] or this method has no
* supporting implementation.
*/
- @ExperimentalWindowApi
fun windowLayoutInfo(@UiContext context: Context): Flow<WindowLayoutInfo> {
- val windowLayoutInfoFlow: Flow<WindowLayoutInfo>? = windowLayoutInfo((context as Activity))
+ val windowLayoutInfoFlow: Flow<WindowLayoutInfo>? = (context as? Activity)
+ ?.let { activity -> windowLayoutInfo(activity) }
return windowLayoutInfoFlow
?: throw NotImplementedError(
message = "Must override windowLayoutInfo(context) and provide an implementation.")
diff --git a/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt b/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt
index 2247c4b..26dd99b 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt
@@ -65,8 +65,4 @@
}
}
}
-
- internal companion object {
- private const val BUFFER_CAPACITY = 10
- }
}
diff --git a/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt b/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt
index b0a0b73..8d8151e 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt
@@ -26,7 +26,6 @@
import androidx.annotation.RestrictTo
import androidx.annotation.UiContext
import androidx.core.view.WindowInsetsCompat
-import androidx.window.core.ExperimentalWindowApi
/**
* An interface to calculate the [WindowMetrics] for an [Activity] or a [UiContext].
@@ -131,14 +130,12 @@
return decorator(WindowMetricsCalculatorCompat)
}
- @ExperimentalWindowApi
@JvmStatic
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun overrideDecorator(overridingDecorator: WindowMetricsCalculatorDecorator) {
decorator = overridingDecorator::decorate
}
- @ExperimentalWindowApi
@JvmStatic
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun reset() {
@@ -159,7 +156,6 @@
}
}
-@ExperimentalWindowApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
interface WindowMetricsCalculatorDecorator {
diff --git a/work/integration-tests/testapp/build.gradle b/work/integration-tests/testapp/build.gradle
index a44e9ab..3984082 100644
--- a/work/integration-tests/testapp/build.gradle
+++ b/work/integration-tests/testapp/build.gradle
@@ -61,7 +61,7 @@
implementation(project(":work:work-multiprocess"))
implementation(project(":work:work-gcm"))
implementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
- implementation("androidx.arch.core:core-runtime:2.1.0")
+ implementation("androidx.arch.core:core-runtime:2.2.0")
implementation("androidx.recyclerview:recyclerview:1.1.0")
implementation(libs.material)
}
diff --git a/work/work-runtime/build.gradle b/work/work-runtime/build.gradle
index 0cc27df..5e8d67b 100644
--- a/work/work-runtime/build.gradle
+++ b/work/work-runtime/build.gradle
@@ -76,7 +76,7 @@
androidTestImplementation(libs.truth)
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testCore)
- androidTestImplementation("androidx.arch.core:core-testing:2.1.0")
+ androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime-testing"))
androidTestImplementation(libs.testRunner)
androidTestImplementation(libs.espressoCore)
diff --git a/work/work-testing/build.gradle b/work/work-testing/build.gradle
index a4e7df7..45e33ef 100644
--- a/work/work-testing/build.gradle
+++ b/work/work-testing/build.gradle
@@ -27,7 +27,7 @@
implementation("androidx.lifecycle:lifecycle-livedata-core:2.5.1")
implementation("androidx.room:room-runtime:2.5.0")
- androidTestImplementation("androidx.arch.core:core-testing:2.1.0")
+ androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testRunner)